├── .gitignore ├── LICENSE ├── README.md └── src ├── commands ├── information │ ├── about.js │ └── ping.js └── staff │ ├── endSupport.js │ └── helpdesk.js ├── config ├── config.js ├── support-settings.js └── token.json ├── index.js ├── package.json └── scripts ├── interactions.js ├── logging.js └── snippets.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | token.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Gideon foxo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Discord Support Bot

2 | 3 | A simple yet customizable and powerful lightweight Discord bot to help automate support by helping users get help to easily answered questions. Say good bye to users asking the same question for the 1,000,00th time! 4 | 5 |

Demo!

6 | 7 | Support bot uses a select menu that allows you to setup 25 questions. Because it sends the user a ephemeral message (blue Clyde message) they can look at the question for however long the user needs! 8 | 9 | ![Its a gif!](https://github.com/Gideon-Foxo/support-bot/assets/50521677/f180e695-0aca-4559-a3ab-fc15e85a4448) 10 | 11 | If the demo does not load [click here](https://github.com/Gideon-Foxo/support-bot/assets/50521677/f180e695-0aca-4559-a3ab-fc15e85a4448) 12 | 13 | Support bot also allows for native logs. Easily see what questions people ask and keep track of who needs support. 14 | 15 | ![Image of logs](https://github.com/Gideon-Foxo/support-bot/assets/50521677/99c8ce7c-976c-4225-b392-46b76f7f260c) 16 | 17 |

Setup and Installation

18 | 19 | 1. Install [node](https://nodejs.org/en/) v16.11.0 or higher. 20 | 21 | 2. [Download the latest release of this project](https://github.com/Gideon-foxo/support-bot/releases) 22 | 23 | 3. CD over to this project on your system and run `npm i`. 24 | 25 | 4. Input all of your settings into [settings.js](https://github.com/Gideon-foxo/support-bot/blob/main/src/config/support-settings.js) (src -> config -> support-settings.js). Optionally, update the colors or playing status in the [config](https://github.com/Gideon-foxo/support-bot/blob/main/src/config/config.js) file (src -> config -> config.js) 26 | 27 | 5. Put your token in [token.json](https://github.com/Gideon-Foxo/support-bot/blob/v2/src/config/token.json) (src -> config -> token.json). If you are confused by what this means please read [this](https://discord.com/developers/docs/getting-started#step-1-creating-an-app) guide. 28 | 29 | 6. CD over to the src folder and run `node index.js` to lunch the bot. It is highly recommended you use a program such as [pm2](https://www.npmjs.com/package/pm2) to run this app. 30 | 31 | 32 |

Contact/Support/Feedback

33 | 34 | To leave suggestions or request support please join [our support server](https://discord.gg/G2sWsCA8nS) or [create a new issue](https://github.com/Gideon-foxo/support-bot/issues). 35 | 36 | If you would like to support my project you can donate at [ko-fi](https://foxo.gay/donate) or directly on [github](https://github.com/sponsors/Gideon-Foxo). 37 | -------------------------------------------------------------------------------- /src/commands/information/about.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config/config.js"); 2 | const Discord = require('discord.js'); 3 | 4 | module.exports = { 5 | 6 | name: 'about', 7 | description: 'Returns information about this bot', 8 | async execute(i) { 9 | 10 | // Define the color 11 | let color = config.accent 12 | if (!color) color = config.embed 13 | 14 | 15 | const embed = new Discord.EmbedBuilder() 16 | .setTitle("About Support Bot") 17 | .setThumbnail(i.client.user.avatarURL({ format: 'png', dynamic: true, size: 2048 })) 18 | .setDescription(`Support Bot is a self hosted, simple and lightweight support desk designed to help automate support. Made by **Gideon_foxo** using [Discord.js](https://discord.js.org/#/) with a MIT license.\n\n[Github](https://github.com/Gideon-Foxo/support-bot) | [Support Server](https://discord.gg/s4BX2qu6Hu) | [Donate](https://foxo.gay/donate)`) 19 | .setColor(color) 20 | 21 | return await i.reply({embeds: [embed]}) 22 | }, 23 | }; -------------------------------------------------------------------------------- /src/commands/information/ping.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config/config.js"); 2 | const Discord = require('discord.js'); 3 | const package = require('../.././package.json'); 4 | const snip = require("../../scripts/snippets"); 5 | 6 | module.exports = { 7 | 8 | name: 'ping', 9 | description: 'Returns my current latency, uptime and version!', 10 | async execute(i) { 11 | 12 | 13 | // Defines the message (this is so we can easily edit it) 14 | let message = null; 15 | 16 | // The first embed that is sent 17 | const firstEmbed = new Discord.EmbedBuilder() 18 | .setColor(config.embed) 19 | .setDescription("Ping?") 20 | 21 | // Changes how it responds based on if the bot in text or slash mode 22 | message = await i.reply({ embeds: [firstEmbed] }) 23 | 24 | // For the embed color 25 | const time = message.createdTimestamp -i.createdTimestamp 26 | 27 | // Define the color! 28 | let color = config.green 29 | if (time > 1200) color = config.red 30 | else if(time > 600) color = config.orange 31 | 32 | // Defines the embed that the message is edited to 33 | const finishEmbed = new Discord.EmbedBuilder() 34 | .setColor(color) 35 | .setTitle("Pong! 🏓") 36 | .setDescription(`**Version**: **${package.version}**\n**Latency**: **${Math.round(time)}**ms\n**Gateway**: **${Math.round(i.client.ws.ping)}**ms\n**Uptime**: ${snip.uptime()}`) 37 | 38 | // Edits the original message 39 | return await message.edit({embeds: [finishEmbed]}) 40 | }, 41 | }; -------------------------------------------------------------------------------- /src/commands/staff/endSupport.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config/config.js"); 2 | const settings = require("../../config/support-settings.js"); 3 | const Discord = require('discord.js'); 4 | const logging = require("../../scripts/logging.js"); 5 | const log = require('dbot-console-logger'); 6 | 7 | module.exports = { 8 | 9 | name: 'endsupport', 10 | description: 'Ends support for a user by removing their support role.', 11 | options: [{type: 6, name: "user", description: "Select a user to end support with!", required: true}], 12 | default_member_permissions: Number(Discord.PermissionFlagsBits.ManageMessages), 13 | async execute(i) { 14 | 15 | 16 | const user = i.options.data[0].user.id 17 | const member = await i.guild.members.fetch(user) 18 | 19 | // If no member 20 | if (!member) { 21 | 22 | const embed = new Discord.EmbedBuilder() 23 | .setColor(config.red) 24 | .setDescription("This user is not in the server, they must be in the server for the user to have roles!") 25 | return await i.reply({ephemeral: true, embeds: [embed]}) 26 | } 27 | 28 | // If member does not have the support role 29 | if (!member.roles.cache.has(settings.support.supportRole)) { 30 | 31 | const embed = new Discord.EmbedBuilder() 32 | .setColor(config.red) 33 | .setDescription("This user does not have the support role!") 34 | return await i.reply({ephemeral: true, embeds: [embed]}) 35 | } 36 | 37 | // Try to remove the role 38 | try { 39 | 40 | await member.roles.remove(settings.support.supportRole) 41 | await i.reply({content: "Support has been removed!", ephemeral: true}) 42 | logging(i, "endSupport", member.displayName) 43 | 44 | } catch (err) { 45 | 46 | log.err("error in end support", err) 47 | 48 | const embed = new Discord.EmbedBuilder() 49 | .setColor(config.red) 50 | .setDescription("I could not remove the role from the user. Please make sure I have the correct permissions to do so or check the console for more information.") 51 | return await i.reply({ephemeral: true, embeds: [embed]}) 52 | } 53 | }, 54 | }; -------------------------------------------------------------------------------- /src/commands/staff/helpdesk.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config/config.js"); 2 | const settings = require("../../config/support-settings.js"); 3 | const Discord = require('discord.js'); 4 | const log = require('dbot-console-logger'); 5 | 6 | module.exports = { 7 | 8 | name: 'helpdesk', 9 | description: 'Spawns the help desk for support.', 10 | default_member_permissions: Number(Discord.PermissionFlagsBits.ManageGuild), 11 | async execute(i) { 12 | 13 | // Define the color 14 | let color = config.accent 15 | if (!color) color = config.embed 16 | 17 | // Defines the select menu 18 | let selectMenu = { 19 | type: 3, 20 | customId: "helpDesk", 21 | placeholder: settings.support.selectMenuName, 22 | options: [] 23 | } 24 | 25 | // Add all of the questions 26 | settings.questions.forEach(q => {selectMenu.options.push({ 27 | label: q.question, 28 | description: (q.questionDescription && q.questionDescription !== "") ? q.questionDescription : undefined, 29 | emoji: (q.emote && q.emote !== "") ? q.emote : undefined, 30 | value: `${selectMenu.options.length}` 31 | })}) 32 | 33 | // Add the support option assuming 34 | if (!settings.support.disableSupport) { 35 | selectMenu.options.push({ 36 | label: settings.support.supportOption.name, 37 | description: (settings.support.supportOption.description && settings.support.supportOption.description !== "") ? settings.support.supportOption.description : undefined, 38 | emoji: (settings.support.supportOption.emote && settings.support.supportOption.emote !== "") ? settings.support.supportOption.emote : undefined, 39 | value: "support" 40 | }) 41 | 42 | } 43 | 44 | // Creates the help desk embed 45 | const helpDesk = new Discord.EmbedBuilder() 46 | .setDescription(settings.embed.description) 47 | .setColor(color) 48 | if (settings.embed.title) helpDesk.setTitle(settings.embed.title) 49 | if (settings.embed.thumbnail) helpDesk.setThumbnail(settings.embed.thumbnail) 50 | if (settings.embed.image) helpDesk.setImage(settings.embed.image) 51 | if (settings.embed.footer) helpDesk.setFooter({ text: settings.embed.footer }) 52 | 53 | 54 | // Do some checks and return errors if the checks fail 55 | if (selectMenu.options.length > 25) { 56 | 57 | const embed = new Discord.EmbedBuilder() 58 | .setColor(config.color) 59 | .setDescription("You are trying to send a select menu with more then 25 options. This is not possible, please update your config!") 60 | return await i.reply({embeds: [embed], ephemeral: true}) 61 | 62 | } else if (selectMenu.options.length === 0) { 63 | 64 | const embed = new Discord.EmbedBuilder() 65 | .setColor(config.color) 66 | .setDescription("You are trying to send a select menu with no options. This is not possible, please update your config!") 67 | return await i.reply({embeds: [embed], ephemeral: true}) 68 | } 69 | 70 | 71 | // Try to send the help desk! 72 | try { 73 | 74 | await i.channel.send({embeds: [helpDesk], components: [{type: 1, components: [selectMenu]}]}) 75 | return i.reply({ephemeral: true, content: "Help desk message sent successfully!"}) 76 | // If any errors catch it and return an error message 77 | } catch (err) { 78 | 79 | log.err("Error in sending help desk", err) 80 | 81 | const embed = new Discord.EmbedBuilder() 82 | .setColor(config.red) 83 | .setDescription("An error occurred, please make sure your config is setup correctly! Check your console for more information") 84 | return await i.reply({embeds: [embed], ephemeral: true}) 85 | } 86 | 87 | 88 | }, 89 | }; -------------------------------------------------------------------------------- /src/config/config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | 3 | presence: "online", // options: online, idle, dnd, invisible 4 | status: "Watching #support | learn more about me at https://github.com/Gideon-foxo/support-bot", 5 | 6 | 7 | 8 | // Colors!!! 9 | //accent: "Hex code here! Dont forget to remove the //!", 10 | embed: "2c2d31", 11 | green: "3BA55C", 12 | red: "F04747", 13 | orange: "FAA61A", 14 | } 15 | 16 | 17 | module.exports = config -------------------------------------------------------------------------------- /src/config/support-settings.js: -------------------------------------------------------------------------------- 1 | const supportConfig = { 2 | 3 | 4 | // Embed for the help desk 5 | embed: { 6 | title: "Help Desk", 7 | description: "Demo help desk. Need help? You came to the right place, to get started just use the select menu below!\n\nSupport can setup to add the user to a private thread or add a role to the user, or both!", 8 | //thumbnail: "image URL for thumbnail in top right corner", 9 | //image: "image URL for big image at bottom", 10 | footer: "This is the small text at the bottom of the embed, it does not support markdown." 11 | }, 12 | 13 | 14 | // The support settings 15 | support: { 16 | disableSupport: false, // Disables support 17 | createThread: true, // Creates a private thread. If you want to change how requestMessage and supportMessage work modify the strings in the support function in interactions.js 18 | supportRole: "ID of role here", // Role given to the user if extra support is requested 19 | requestMessage: "A support ticket has been made", // If create thread is enabled then the created thread will be automatically added to the end of the default message. 20 | supportMessage: "We will be with you shortly, in the meantime please ask you question in as much detail as you can!", 21 | supportChannel: "Support Message Channel ID", 22 | 23 | 24 | selectMenuName: "Need help? Select a question!", 25 | supportOption: { 26 | name: "My question is not answered", 27 | description: "I need more support!", 28 | emote: "❓" 29 | }, 30 | 31 | 32 | loggingChannel: "ID of logging channel", // Channel for logging all questions and support status 33 | notificationRole: "ID of staff role", // Role for staff 34 | }, 35 | 36 | 37 | // Array of questions and answers for the help desk 38 | questions: [ 39 | { 40 | question: "What does this bot do?", 41 | questionDescription: "How will it help users?", 42 | emote: "🤖", 43 | 44 | answer: "This bot helps users answer questions in an automated format! No more pointing users to a long faq they wont really read!!", 45 | image: "https://cdn.discordapp.com/attachments/995986581460615178/1152729799769464863/Discord_ur4cd1FSFI.png", 46 | directSupport: false 47 | }, 48 | { 49 | question: "What does the direct support option do?", 50 | //questionDescription: "", 51 | //emote: "", 52 | 53 | answer: "It adds a button to answer response that allows a user to directly request support without having to go back to the select menu and hit extra support needed.", 54 | //image: "", 55 | directSupport: true 56 | }, 57 | { 58 | question: "I still need help with this bot, where do I go?", 59 | //questionDescription: "", 60 | //emote: "", 61 | 62 | answer: "You can either ask in our [support server](https://discord.gg/s4BX2qu6Hu) or create a new [pull request](https://github.com/Gideon-Foxo/support-bot/pulls) on our github page!", 63 | //image: "", 64 | directSupport: false 65 | }, 66 | { 67 | question: "How can I donate to this project?", 68 | //questionDescription: "", 69 | //emote: "", 70 | 71 | answer: "You can either give me a [coffee](https://foxo.gay/donate) or donate on [github](https://github.com/sponsors/Gideon-Foxo)!", 72 | //image: "", 73 | directSupport: false 74 | }, 75 | { 76 | question: "How many questions can I set?", 77 | //questionDescription: "", 78 | //emote: "", 79 | 80 | answer: "You can set up to 24 questions, or 25 if you do disable the extra support option.", 81 | //image: "", 82 | directSupport: false 83 | }, 84 | { 85 | question: "Can I set custom emotes?", 86 | //questionDescription: "", 87 | emote: "", 88 | 89 | answer: "Yes you can! put \ before an emote and then send it and copy and past the result, it should look something like ``", 90 | //image: "", 91 | directSupport: false 92 | }, 93 | ] 94 | 95 | } 96 | 97 | 98 | module.exports = supportConfig -------------------------------------------------------------------------------- /src/config/token.json: -------------------------------------------------------------------------------- 1 | { 2 | "token": "put-your-token-here-DO-NOT-PUBLISH-THIS-TO-GITHUB-OR-SHARE-ANYWHERE" 3 | } 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // This allows pm2 and chalk to work at the same time. Without it when using pm2 chalk does not do anything. 2 | process.env.FORCE_COLOR = "true"; 3 | 4 | // Define modules 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const Discord = require('discord.js'); 8 | const log = require('dbot-console-logger'); 9 | 10 | // Require fillesssss 11 | const interactions = require("./scripts/interactions") 12 | const token = require('./config/token.json'); 13 | const package = require('./package.json'); 14 | const config = require("./config/config"); 15 | const settings = require("./config/support-settings"); 16 | 17 | 18 | 19 | // Defines the client or bot instance 20 | const client = new Discord.Client({ 21 | allowedMentions: { parse: [] }, 22 | intents: [Discord.GatewayIntentBits.GuildMembers, Discord.GatewayIntentBits.Guilds] 23 | }); 24 | client.commands = new Discord.Collection(); 25 | 26 | // Define client uptime 27 | client.startTime = Date.now() 28 | 29 | 30 | 31 | // This triggers when the bot is "ready" 32 | client.on('ready', async (client) => { 33 | 34 | 35 | // Command loading! 36 | // Add all new folders here 37 | client.cmdmodules = fs.readdirSync(path.join(__dirname, './commands/')) 38 | 39 | // Loops thought the folders under 'commands' and loads the command files as long as they are js files 40 | for (const folder of client.cmdmodules) { 41 | let filesInFolder = fs.readdirSync(path.join(__dirname, './commands/', folder)).filter(file => file.endsWith('.js')) 42 | for (const file of filesInFolder) { 43 | const commandexport = require(`./commands/${folder}/${file}`) 44 | commandexport.module = folder 45 | // If the file starts with _ ignore it 46 | if (commandexport?.name?.startsWith("_") || !commandexport?.name || file.startsWith("_")) continue 47 | // If the command is end support and that type of support (if any) does not exist 48 | if (commandexport?.name === "endsupport" && (settings.support.disableSupport || !settings.support.supportRole)) continue 49 | client.commands.set(commandexport.name, commandexport) 50 | //log.debug(`Loaded command ${log.c.bold(commandexport.name)}`) 51 | } 52 | } 53 | 54 | 55 | // Defines all of the commands in one array 56 | let slashCommands = [...client.commands.values()].map(c => ({ ...c })) 57 | 58 | 59 | try { 60 | // Defines the where to send the commands to 61 | const rest = new Discord.REST().setToken(token.token); 62 | 63 | // Tell Discord our commands 64 | await rest.put( 65 | Discord.Routes.applicationCommands(client.user.id), 66 | { body: slashCommands }, 67 | ); 68 | // Catch any errors if Discord is unhappy 69 | } catch (error) { 70 | log.err("Error attempting to send slash data", error) 71 | } 72 | 73 | await client.presence.set({ activities: [{ name: config.status, type: Discord.ActivityType.Custom }], status: config.presence }) 74 | // When everything is fully done send the the console log that I am ready! 75 | log.info(`I am now ${log.chalk.green("ready")} logged into ${log.chalk.bold(client.user.tag)} running version ${log.chalk.bold(package.version)} in ${log.chalk.bold(client.guilds.cache.size)} servers`) 76 | }) 77 | 78 | 79 | 80 | // The interaction event! 81 | client.on('interactionCreate', async i => { 82 | 83 | 84 | // If slash command 85 | if (i.isCommand()) { 86 | let command = null 87 | try { 88 | 89 | // Fetch the command 90 | command = client.commands.get(i.commandName) 91 | 92 | await command.execute(i); 93 | 94 | // Catch the errors! This is a really basic system I plan to make it much nicer later 95 | } catch (err) { 96 | 97 | log.err(`Error in command ${command?.name}`, err) 98 | 99 | const embed = new Discord.EmbedBuilder() 100 | .setColor(config.orange) 101 | .setDescription("⚠️ An unexpected error occurred, please try again. If the issue persists please let an administrator know!") 102 | return await i.reply({embeds: [embed], ephemeral: true}) 103 | } 104 | // If an interaction thats not a slash command! 105 | } else { 106 | try { 107 | 108 | await interactions(i) 109 | 110 | // Catch any errors that might happen 111 | } catch (err) { 112 | log.err("Error in the interaction handler!", err) 113 | 114 | const embed = new Discord.EmbedBuilder() 115 | .setColor(config.orange) 116 | .setDescription("⚠️ An unexpected error occurred, please try again. If the issue persists please let an administrator know!") 117 | return await i.reply({embeds: [embed], ephemeral: true}) 118 | } 119 | } 120 | }) 121 | 122 | 123 | 124 | // Log in! Log in! Log in! Log in! Log in! Log in! 125 | client.login(token.token) 126 | 127 | 128 | // Allows the client to be accessed as a module 129 | module.exports = client -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "support-bot", 3 | "version": "2.0.2", 4 | "description": "A simple and lightweight Discord support bot", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/Gideon-foxo/support-bot.git" 9 | }, 10 | "keywords": [ 11 | "Discord-bot", 12 | "Discord-support-bot", 13 | "Discordbot" 14 | ], 15 | "author": "Gideon Foxo", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/Gideon-foxo/support-bot/issues" 19 | }, 20 | "homepage": "https://github.com/Gideon-foxo/support-bot#readme", 21 | "dependencies": { 22 | "dbot-console-logger": "1.0.2", 23 | "discord.js": "^14.13.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/scripts/interactions.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | const log = require('dbot-console-logger'); 3 | const config = require('../config/config.js'); 4 | const settings = require("../config/support-settings.js"); 5 | const logging = require("./logging.js"); 6 | 7 | 8 | async function handler (i) { 9 | 10 | // Define the ID 11 | let ID = i.customId?.split(" ") 12 | const client = i.client 13 | 14 | 15 | // When support is called. This is because support can be called from more then one function 16 | async function support() { 17 | 18 | 19 | // If support is disabled send a warning message 20 | if (settings.support.disableSupport) { 21 | 22 | const embed = new Discord.EmbedBuilder() 23 | .setColor(config.red) 24 | .setDescription("Support was disabled! Please let an administrator know the help desk is outdated!") 25 | await i.reply({embeds: [embed], ephemeral: true}) 26 | return log.warn("A user requested support but support is disabled! Please make sure your help desk message is updated!") 27 | } 28 | 29 | // If support requests a role 30 | if (settings.support.supportRole) { 31 | 32 | // Catch any errors 33 | try { 34 | 35 | // If member already has the support role 36 | if (i.member.roles.cache.has(settings.support.supportRole)) { 37 | 38 | const embed = new Discord.EmbedBuilder() 39 | .setColor(config.red) 40 | .setDescription("You have already requested more support, you can not request it again until it is resolved!") 41 | return await i.reply({embeds: [embed], ephemeral: true}) 42 | } 43 | 44 | await i.member.roles.add(settings.support.supportRole) 45 | } catch (err) { 46 | log.error("Can not add role to user when support is requested. Make sure you are giving a valid role ID, and that the bot has permissions and is above the role.", err) 47 | } 48 | } 49 | 50 | // If support requests a private thread 51 | if (settings.support.createThread) { 52 | 53 | // Define the thread name 54 | let name = i.user.globalName 55 | if (!name) name = i.user.tag 56 | // Catch any errors 57 | try { 58 | const channel = await i.channel.threads.create({name: name, type: Discord.ChannelType.PrivateThread}) 59 | await channel.send({content: `<@${i.user.id}> ${settings.support.supportMessage}`, allowedMentions: {users: [i.user.id]} }) 60 | await i.reply({ephemeral: true, content: `${settings.support.requestMessage} <#${channel.id}>`}) 61 | } catch (err) { 62 | log.error("I can not make a thread, please make sure I have the correct permissions needed!", err) 63 | } 64 | // If not threads 65 | } else { 66 | 67 | await i.reply({content: settings.support.requestMessage, ephemeral: true}) 68 | 69 | if (settings.support.supportChannel) { 70 | try { 71 | const channel = await client.channels.fetch(settings.support.supportChannel) 72 | await channel.send(settings.support.supportMessage) 73 | // If error 74 | } catch (err) { 75 | log.error("Error with sending support message in the support channel!", err) 76 | } 77 | } 78 | } 79 | } 80 | 81 | 82 | // If select menu then help desk. Yes, I know this is somewhat hard coded, but if more features get added down the line to where their will be more then one select menu then this will be updated 83 | if (i.isStringSelectMenu()) { 84 | 85 | ID = i?.values[0] 86 | 87 | 88 | // If support 89 | if (ID === "support") { 90 | support() 91 | logging(i, "support") 92 | // Otherwise if a question 93 | } else { 94 | 95 | let color = config.accent 96 | if (!color) color = config.embed 97 | 98 | const button = [{ style: Discord.ButtonStyle.Primary, label: 'I need more support', type: 2, customId: `${ID}` }] 99 | 100 | const embed = new Discord.EmbedBuilder() 101 | .setColor(color) 102 | .setDescription(settings.questions[ID].answer) 103 | if (settings.questions[ID].image) embed.setImage(settings.questions[ID].image) 104 | 105 | if (settings.questions[ID].directSupport) await i.reply({embeds: [embed], components: [{type: 1, components: button}], ephemeral: true}) 106 | else await i.reply({embeds: [embed], ephemeral: true}) 107 | 108 | logging(i, "question") 109 | } 110 | 111 | 112 | 113 | 114 | // If button (extra support) 115 | } else if (i.isButton()) { 116 | support() 117 | logging(i, "support") 118 | } 119 | } 120 | 121 | 122 | 123 | module.exports = handler -------------------------------------------------------------------------------- /src/scripts/logging.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | const log = require('dbot-console-logger'); 3 | const config = require('../config/config.js'); 4 | const settings = require("../config/support-settings.js"); 5 | 6 | 7 | // Logging function for the help desk 8 | async function logger (i, action, data) { 9 | 10 | if (!settings.support.loggingChannel) return 11 | 12 | try { 13 | 14 | const client = i.client 15 | const channel = await client.channels.fetch(settings.support.loggingChannel) 16 | let name = i.user.globalName 17 | if (!name) name = i.user.tag 18 | 19 | 20 | if (action === "support") { 21 | 22 | const embed = new Discord.EmbedBuilder() 23 | .setColor(config.orange) 24 | .setAuthor({ name: `${name} (${i.user.id})`, iconURL: i.user.displayAvatarURL({ format: 'png', dynamic: true, size: 2048 }), url: `https://discord.com/users/${i.user.id}`}) 25 | .setDescription(`**Extra support requested**${(i.isButton()) ? ` from question \`${settings.questions[i.customId].question}\`` : ""}.`) 26 | 27 | if (settings.support.notificationRole) return await channel.send({embeds: [embed], content: `<@&${settings.support.notificationRole}>`, allowedMentions: {roles: [settings.support.notificationRole]} }) 28 | else return await channel.send({embeds: [embed]}) 29 | 30 | // If command to remove support 31 | } else if (action === "endSupport") { 32 | 33 | const embed = new Discord.EmbedBuilder() 34 | .setColor(config.green) 35 | .setAuthor({ name: `${name} (${i.user.id})`, iconURL: i.user.displayAvatarURL({ format: 'png', dynamic: true, size: 2048 }), url: `https://discord.com/users/${i.user.id}`}) 36 | .setDescription(`Support was ended for **${data}**.`) 37 | 38 | return await channel.send({embeds: [embed]}) 39 | 40 | // If a question 41 | } else { 42 | 43 | const embed = new Discord.EmbedBuilder() 44 | .setColor(config.embed) 45 | .setAuthor({ name: `${name} (${i.user.id})`, iconURL: i.user.displayAvatarURL({ format: 'png', dynamic: true, size: 2048 }), url: `https://discord.com/users/${i.user.id}`}) 46 | .setDescription(`**Asked**: ${settings.questions[i.values[0]].question}`) 47 | 48 | return await channel.send({embeds: [embed]}) 49 | } 50 | } catch (err) { 51 | log.error("Something broke in the logging system, please make sure you have a correct channel ID, and that I have perms to send messages in that channel", err) 52 | 53 | } 54 | } 55 | 56 | 57 | module.exports = logger -------------------------------------------------------------------------------- /src/scripts/snippets.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | const log = require('dbot-console-logger'); 3 | const config = require('../config/config.js'); 4 | const client = require('../index.js'); 5 | 6 | 7 | 8 | // This returns the clients uptime! 9 | function uptime() { 10 | return timeConversion(Date.now() - client.startTime) 11 | } 12 | 13 | 14 | 15 | // Time converter 16 | function timeConversion(millisec) { 17 | 18 | var seconds = (millisec / 1000).toFixed(0); 19 | var minutes = (millisec / (1000 * 60)).toFixed(0); 20 | var hours = (millisec / (1000 * 60 * 60)).toFixed(0); 21 | var days = (millisec / (1000 * 60 * 60 * 24)).toFixed(0); 22 | var years = (millisec / (1000 * 60 * 60 * 24 * 365)).toFixed(0); 23 | 24 | if (seconds < 60) return `**${seconds}** Seconds`; 25 | else if (minutes < 60) return `**${minutes}** Minute${(minutes > 1) ? "s" : ""}`; 26 | else if (hours < 24) return `**${hours}** Hour${(hours > 1) ? "s" : ""}`; 27 | else if (days < 365) return `**${days}** Day${(days > 1) ? "s" : ""}` 28 | else return `**${years}** Year${(years > 1) ? "s" : ""}` 29 | } 30 | 31 | 32 | 33 | 34 | 35 | module.exports = { 36 | uptime 37 | } --------------------------------------------------------------------------------