├── .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 | 
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 | 
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 | }
--------------------------------------------------------------------------------