├── .dockerignore ├── .env.example ├── .gitignore ├── .vscode └── extensions.json ├── CountingLogics ├── CountingResolver.js └── ResolveMessages.js ├── Dockerfile ├── Events ├── Client │ └── Ready.js ├── Counting │ └── CountEvent.js ├── Interaction │ ├── AutoComplete.js │ ├── ButtonCreate.js │ ├── InteractionCreate.js │ └── ModalCreate.js ├── Log │ └── InteractionLog.js └── Topgg │ └── VoteReward.js ├── Interactions ├── Components │ ├── Buttons │ │ └── Delete.js │ └── Modals │ │ └── DeleteConfirm.js └── SlashCommands │ ├── Admin │ ├── Language.js │ ├── Move.js │ ├── Setup.js │ └── Toggle.js │ ├── Dev │ ├── Eval.js │ ├── GuildLeave.js │ └── Reload.js │ ├── Info │ ├── Help.js │ └── Ping.js │ └── User │ ├── Leaderboard.js │ ├── Rules.js │ ├── Stats.js │ └── Vote.js ├── README.md ├── Schemas ├── Bot │ └── BotDatas.js ├── Counting │ ├── GameData.js │ └── SetupData.js ├── Server │ └── LanguageData.js ├── User │ └── UserData.js └── index.js ├── Structures ├── Classes │ ├── BaseCommand.js │ ├── BaseComponent.js │ ├── BaseEvent.js │ └── BotClient.js ├── Functions │ ├── JsonFind.js │ ├── Logger.js │ ├── PaginationEmbed.js │ ├── VoteLog.js │ └── index.js └── Handlers │ ├── CommandHandler.js │ ├── ComponentHandler.js │ ├── ErrorHandler.js │ ├── EventHandler.js │ └── LanguageHandler.js ├── bot.js ├── config.js ├── index.js ├── locales ├── bn │ ├── command.json │ ├── component.json │ └── system.json ├── en │ ├── command.json │ ├── component.json │ └── system.json ├── pt │ ├── command.json │ ├── component.json │ └── system.json └── resources.js └── package.json /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | token="your bot token" 2 | mongoUrl="mongodb+srv://xyz" 3 | logWebhook="https://discord.com/api/webhooks/xyz" 4 | redis="redis://xyz" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .env -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-vscode.vscode-typescript-next" 4 | ] 5 | } -------------------------------------------------------------------------------- /CountingLogics/CountingResolver.js: -------------------------------------------------------------------------------- 1 | const { ResolveMassages } = require("./ResolveMessages"); 2 | const math = require("mathjs"); 3 | 4 | const reaction = { 5 | success: "✅", 6 | fail: "❌", 7 | save: "⛑️", 8 | warn: "⚠️", 9 | }; 10 | 11 | class CountingResolver { 12 | constructor(message, setupData, gameData, userData, botData, lng, client) { 13 | this.msg = message; 14 | this.message = message.content; 15 | this.counter = message.author.id; 16 | this.guildId = message.guild.id; 17 | this.channelId = message.channel.id; 18 | this.lng = lng; 19 | this.math = setupData.math; 20 | this.numOnly = setupData.numOnly; 21 | this.guildSaves = gameData.saves; 22 | this.lastCounter = gameData.lastCounter; 23 | this.count = gameData.count + 1; 24 | this.userSaves = userData.saves; 25 | this.highestCount = gameData.highestCount; 26 | this.reaction = gameData.reaction; 27 | this.gameData = gameData; 28 | this.userData = userData; 29 | this.botData = botData; 30 | this.client = client; 31 | } 32 | messageConvart() { 33 | let count = this.message.replace(/\*\*|~~|__|_|\\/g, "").split(" ")[0]; 34 | 35 | if (count == "") return; 36 | if (this.math == true) { 37 | try { 38 | count = math.evaluate(count); 39 | } catch (error) { 40 | return; 41 | } 42 | } 43 | this.value = Number(count); 44 | } 45 | countResolve() { 46 | if (this.counter !== this.lastCounter && this.count == this.value) { 47 | this.output = "success"; 48 | } 49 | if ( 50 | this.value !== this.count && 51 | this.userSaves >= 1 && 52 | this.counter !== this.lastCounter 53 | ) 54 | this.output = "rongUseUserSave"; 55 | if ( 56 | this.value !== this.count && 57 | this.counter !== this.lastCounter && 58 | this.guildSaves >= 1 && 59 | this.userSaves <= 1 60 | ) 61 | this.output = "rongUseGuildSave"; 62 | if ( 63 | this.value !== this.count && 64 | this.userSaves < 1 && 65 | this.guildSaves < 1 && 66 | this.counter !== this.lastCounter 67 | ) 68 | this.output = "RongFail"; 69 | if (this.counter == this.lastCounter && this.userSaves >= 1) 70 | this.output = "B2BUseUserSave"; 71 | if ( 72 | this.counter == this.lastCounter && 73 | this.guildSaves >= 1 && 74 | this.userSaves <= 1 75 | ) 76 | this.output = "B2BUseGuildSave"; 77 | if ( 78 | this.counter == this.lastCounter && 79 | this.userSaves < 1 && 80 | this.guildSaves < 1 81 | ) 82 | this.output = "B2BFail"; 83 | if (this.count == 1 && this.value !== this.count) this.output = "warning"; 84 | } 85 | sendMessage() { 86 | const msg = new ResolveMassages(this); 87 | msg.sendMessage(); 88 | } 89 | react() { 90 | switch (this.output) { 91 | case "success": 92 | if (this.highestCount < this.value) { 93 | this.msg.react("🏆"); 94 | } else this.msg.react(this.reaction.success); 95 | break; 96 | case "rongUseUserSave": 97 | case "rongGuildUserSave": 98 | case "B2BUseUserSave": 99 | case "B2BUseUserSave": 100 | this.msg.react(this.reaction.save); 101 | break; 102 | case "RongFail": 103 | case "B2BFail": 104 | this.msg.react(this.reaction.fail); 105 | break; 106 | case "warning": 107 | this.msg.react(this.reaction.warn); 108 | break; 109 | default: 110 | break; 111 | } 112 | } 113 | async updateDB() { 114 | const db = this.client.db; 115 | let updateUser; 116 | let updateGame; 117 | let updateBot; 118 | switch (this.output) { 119 | case "success": 120 | updateUser = await db.userDatas.findOneAndUpdate( 121 | { userId: this.counter }, 122 | { 123 | score: this.userData.score + 1, 124 | name: this.msg.author.username, 125 | "count.right": this.userData.count.right + 1, 126 | }, 127 | { upsert: true, new: true } 128 | ); 129 | await this.client.redis.set( 130 | this.counter + "user", 131 | JSON.stringify(updateUser), 132 | "EX", 133 | 60 134 | ); 135 | 136 | updateGame = await db.gameDatas.findOneAndUpdate( 137 | { guildId: this.guildId }, 138 | { 139 | name: this.msg.guild.name, 140 | count: this.count, 141 | lastCounter: this.counter, 142 | "totalCount.right": this.gameData.totalCount.right + 1, 143 | highestCount: 144 | this.highestCount < this.value 145 | ? this.gameData.count 146 | : this.highestCount, 147 | }, 148 | { upsert: true, new: true } 149 | ); 150 | await this.client.redis.set( 151 | this.guildId + "game", 152 | JSON.stringify(updateGame), 153 | "EX", 154 | 60 155 | ); 156 | updateBot = await db.botDatas.findOneAndUpdate( 157 | { password: "jasonmidul" }, 158 | { count: this.botData.count + 1 }, 159 | { upsert: true, new: true } 160 | ); 161 | await this.client.redis.set( 162 | "jasonmidul", 163 | JSON.stringify(updateBot), 164 | "EX", 165 | 60 166 | ); 167 | 168 | break; 169 | case "rongUseUserSave": 170 | case "B2BUseUserSave": 171 | updateUser = await db.userDatas.findOneAndUpdate( 172 | { userId: this.counter }, 173 | { 174 | score: this.userData.score - 1, 175 | saves: this.userData.saves - 1, 176 | "count.rong": this.userData.count.rong + 1, 177 | }, 178 | { upsert: true, new: true } 179 | ); 180 | await this.client.redis.set( 181 | this.counter + "user", 182 | JSON.stringify(updateUser), 183 | "EX", 184 | 60 185 | ); 186 | updateGame = await db.gameDatas.findOneAndUpdate( 187 | { guildId: this.guildId }, 188 | { 189 | "totalCount.rong": this.gameData.totalCount.rong + 1, 190 | }, 191 | { upsert: true, new: true } 192 | ); 193 | await this.client.redis.set( 194 | this.guildId + "game", 195 | JSON.stringify(updateGame), 196 | "EX", 197 | 60 198 | ); 199 | break; 200 | case "rongGuildUserSave": 201 | case "B2BUseUserSave": 202 | updateUser = await db.userDatas.findOneAndUpdate( 203 | { userId: this.counter }, 204 | { 205 | score: this.userData.score - 1, 206 | "count.rong": this.userData.count.rong + 1, 207 | }, 208 | { upsert: true, new: true } 209 | ); 210 | await this.client.redis.set( 211 | this.counter + "user", 212 | JSON.stringify(updateUser), 213 | "EX", 214 | 60 215 | ); 216 | updateGame = await db.gameDatas.findOneAndUpdate( 217 | { guildId: this.guildId }, 218 | { 219 | "totalCount.rong": this.gameData.totalCount.rong + 1, 220 | saves: this.gameData.saves - 1, 221 | }, 222 | { upsert: true, new: true } 223 | ); 224 | await this.client.redis.set( 225 | this.guildId + "game", 226 | JSON.stringify(updateGame), 227 | "EX", 228 | 60 229 | ); 230 | 231 | break; 232 | case "RongFail": 233 | case "B2BFail": 234 | updateUser = await db.userDatas.findOneAndUpdate( 235 | { userId: this.counter }, 236 | { 237 | score: 0, 238 | "count.rong": this.userData.count.rong + 1, 239 | }, 240 | { upsert: true, new: true } 241 | ); 242 | await this.client.redis.set( 243 | this.counter + "user", 244 | JSON.stringify(updateUser), 245 | "EX", 246 | 60 247 | ); 248 | updateGame = await db.gameDatas.findOneAndUpdate( 249 | { guildId: this.guildId }, 250 | { 251 | count: 0, 252 | lastCounter: "none", 253 | "totalCount.rong": this.gameData.totalCount.rong + 1, 254 | }, 255 | { upsert: true, new: true } 256 | ); 257 | await this.client.redis.set( 258 | this.guildId + "game", 259 | JSON.stringify(updateGame), 260 | "EX", 261 | 60 262 | ); 263 | break; 264 | default: 265 | break; 266 | } 267 | } 268 | } 269 | 270 | module.exports = { CountingResolver }; 271 | -------------------------------------------------------------------------------- /CountingLogics/ResolveMessages.js: -------------------------------------------------------------------------------- 1 | const { EmbedBuilder, Colors } = require("discord.js"); 2 | const { t } = require("i18next"); 3 | 4 | class ResolveMassages { 5 | constructor(res) { 6 | this.lng = res.lng; 7 | this.counter = res.counter; 8 | this.count = res.count; 9 | this.userSaves = res.userSaves; 10 | this.guildSaves = res.guildSaves; 11 | this.reason = res.output; 12 | this.message = res.msg; 13 | } 14 | async sendMessage() { 15 | const lng = this.lng; 16 | const user = this.counter; 17 | const nextNum = this.count; 18 | const message = this.message; 19 | const embed = new EmbedBuilder().setColor(Colors.Yellow); 20 | 21 | switch (this.reason) { 22 | case "rongUseUserSave": 23 | await message.channel.send( 24 | t("event.counting.userSaveUseW", { 25 | lng, 26 | user, 27 | nextNum, 28 | leftSave: this.userSaves - 1, 29 | totalSave: this.userSaves, 30 | }) 31 | ); 32 | break; 33 | case "B2BUseUserSave": 34 | await message.channel.send( 35 | t("event.counting.userSaveUseB2B", { 36 | lng, 37 | user, 38 | nextNum, 39 | leftSave: this.userSaves - 1, 40 | totalSave: this.userSaves, 41 | }) 42 | ); 43 | break; 44 | case "rongGuildUserSave": 45 | await message.channel.send( 46 | t("event.counting.guildSaveUseW", { 47 | lng, 48 | user, 49 | nextNum, 50 | leftSave: this.guildSaves - 1, 51 | totalSave: this.guildSaves, 52 | }) 53 | ); 54 | break; 55 | case "B2BUseUserSave": 56 | await message.channel.send( 57 | t("event.counting.guildSaveUseB2B", { 58 | lng, 59 | user, 60 | nextNum, 61 | leftSave: this.guildSaves - 1, 62 | totalSave: this.guildSaves, 63 | }) 64 | ); 65 | break; 66 | case "RongFail": 67 | embed.setDescription( 68 | t("event.counting.embed.des", { 69 | lng, 70 | link: "https://top.gg/bot/1293072781491044415/vote", 71 | }) 72 | ); 73 | await message.channel.send({ 74 | content: t("event.counting.wrongNum", { 75 | lng, 76 | user, 77 | breakAt: this.count - 1, 78 | }), 79 | embeds: [embed], 80 | }); 81 | break; 82 | case "B2BFail": 83 | embed.setDescription( 84 | t("event.counting.embed.des", { 85 | lng, 86 | link: "https://top.gg/bot/1293072781491044415/vote", 87 | }) 88 | ); 89 | await message.channel.send({ 90 | content: t("event.counting.B2BCount", { 91 | lng, 92 | user, 93 | breakAt: this.count - 1, 94 | }), 95 | embeds: [embed], 96 | }); 97 | break; 98 | case "warning": 99 | const msg = await message.channel.send( 100 | t("event.counting.startCount", { 101 | lng, 102 | user, 103 | }) 104 | ); 105 | 106 | setTimeout(() => { 107 | msg.delete(); 108 | }, 3_000); 109 | break; 110 | default: 111 | break; 112 | } 113 | } 114 | } 115 | module.exports = { ResolveMassages }; 116 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22.11.0 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /Events/Client/Ready.js: -------------------------------------------------------------------------------- 1 | const Event = require("../../Structures/Classes/BaseEvent"); 2 | const { CommandHandler } = require("../../Structures/Handlers/CommandHandler"); 3 | const { 4 | ComponentHandler, 5 | } = require("../../Structures/Handlers/ComponentHandler"); 6 | const { loadLanguages } = require("../../Structures/Handlers/LanguageHandler"); 7 | const { Events, ActivityType, PresenceUpdateStatus } = require("discord.js"); 8 | const { Logger } = require("../../Structures/Functions/index"); 9 | const logger = new Logger(); 10 | 11 | class Ready extends Event { 12 | constructor(client) { 13 | super(client, { 14 | name: Events.ClientReady, 15 | }); 16 | } 17 | 18 | async execute(client) { 19 | const { loadCommands } = new CommandHandler(); 20 | const { loadComponents } = new ComponentHandler(); 21 | 22 | try { 23 | await loadLanguages(); 24 | await loadCommands(client, client.config.deploySlashOnReady); 25 | await loadComponents(client); 26 | } catch (error) { 27 | logger.error(error); 28 | } 29 | 30 | logger.success(`${client.user.username}(#${client.cluster.id}) is ready!`); 31 | 32 | try { 33 | await client.db.ConnectMongo(client); 34 | } catch (error) { 35 | logger.error(error); 36 | } 37 | let botData = await client.db.botDatas.findOne({ password: "jasonmidul" }); 38 | if (!botData) await client.db.botDatas.create({ password: "jasonmidul" }); 39 | 40 | setInterval(async () => { 41 | const setups = await client.db.setupDatas.find(); 42 | botData = await client.db.botDatas.findOne({ password: "jasonmidul" }); 43 | 44 | const activitys = [ 45 | { 46 | name: `${setups.length} Setups`, 47 | type: ActivityType.Listening, 48 | }, 49 | { 50 | name: `${botData.count} Counts`, 51 | type: ActivityType.Playing, 52 | }, 53 | ]; 54 | const activity = activitys[Math.floor(Math.random() * activitys.length)]; 55 | await client.user.setActivity(activity); 56 | await client.user.setStatus(PresenceUpdateStatus.Idle); 57 | }, 10_000); 58 | } 59 | } 60 | 61 | module.exports = Ready; 62 | -------------------------------------------------------------------------------- /Events/Counting/CountEvent.js: -------------------------------------------------------------------------------- 1 | const Event = require("../../Structures/Classes/BaseEvent"); 2 | const { 3 | Events, 4 | PermissionFlagsBits, 5 | EmbedBuilder, 6 | Colors, 7 | } = require("discord.js"); 8 | const { Logger } = require("../../Structures/Functions/index"); 9 | const { CountingResolver } = require("../../CountingLogics/CountingResolver"); 10 | const logger = new Logger(); 11 | const { t } = require("i18next"); 12 | 13 | class CountingEvent extends Event { 14 | constructor(client) { 15 | super(client, { 16 | name: Events.MessageCreate, 17 | }); 18 | } 19 | 20 | async execute(message) { 21 | if (message.guild === null) return; 22 | const { client } = this; 23 | const db = client.db; 24 | 25 | const setupCach = await client.redis.get(message.guild.id + "setup"); 26 | let setupData; 27 | if (setupCach) { 28 | setupData = JSON.parse(setupCach); 29 | } else { 30 | setupData = await db.setupDatas.findOne({ 31 | guildId: message.guild.id, 32 | }); 33 | if (setupData) { 34 | await client.redis.set( 35 | message.guild.id + "setup", 36 | JSON.stringify(setupData), 37 | "EX", 38 | 60 39 | ); 40 | } 41 | } 42 | 43 | if (!setupData || message.channel.id !== setupData.setupChannel) return; 44 | const lngCach = await client.redis.get(message.guild.id + "lng"); 45 | let languageData; 46 | if (lngCach) { 47 | languageData = JSON.parse(lngCach); 48 | } else { 49 | languageData = await db.languageDatas.findOne({ 50 | guildId: message.guild.id, 51 | }); 52 | if (!languageData && message.guild.id !== null) { 53 | await db.languageDatas.create({ 54 | guildId: message.guild.id, 55 | lng: "en", 56 | }); 57 | languageData = await db.languageDatas.findOne({ 58 | guildId: message.guild.id, 59 | }); 60 | } 61 | await client.redis.set( 62 | message.guild.id + "lng", 63 | JSON.stringify(languageData), 64 | "EX", 65 | 60 66 | ); 67 | } 68 | const lng = message.guild.id == null ? "en" : languageData.lng; 69 | if (message.author.id == client.user.id) return; 70 | if (message.author.bot) { 71 | if (setupData.numOnly == true) { 72 | if ( 73 | message.guild.members.me.permissions.has( 74 | PermissionFlagsBits.ManageMessages 75 | ) 76 | ) { 77 | setTimeout(() => { 78 | message.delete(); 79 | }, 2_000); 80 | return; 81 | } else { 82 | setupData.numOnly = false; //need up here dhjdjhdjdtjdgj 83 | message.channel.send( 84 | t("event.counting.numOnlyDisable", { 85 | lng, 86 | }) 87 | ); 88 | } 89 | } else return; 90 | } 91 | const gameCach = await client.redis.get(message.guild.id + "game"); 92 | let gameData; 93 | if (gameCach) { 94 | gameData = JSON.parse(gameCach); 95 | } else { 96 | gameData = await db.gameDatas.findOne({ 97 | guildId: message.guild.id, 98 | }); 99 | } 100 | const botCach = await client.redis.get("jasonmidul"); 101 | let botData; 102 | if (botCach) { 103 | botData = JSON.parse(botCach); 104 | } else { 105 | botData = await db.botDatas.findOne({ password: "jasonmidul" }); 106 | } 107 | const userCach = await client.redis.get(message.author.id + "user"); 108 | let userData; 109 | if (userCach) { 110 | userData = JSON.parse(userCach); 111 | } else { 112 | userData = await db.userDatas.findOne({ 113 | userId: message.author.id, 114 | }); 115 | } 116 | 117 | if (!userData) { 118 | await db.userDatas.create({ 119 | userId: message.author.id, 120 | saves: 2, 121 | vote: { 122 | count: 2, 123 | }, 124 | }); 125 | userData = await db.userDatas.findOne({ 126 | userId: message.author.id, 127 | }); 128 | } 129 | const game = new CountingResolver( 130 | message, 131 | setupData, 132 | gameData, 133 | userData, 134 | botData, 135 | lng, 136 | client 137 | ); 138 | game.messageConvart(); 139 | if (!game.value) { 140 | if (game.numOnly == true) { 141 | if ( 142 | message.guild.members.me.permissions.has( 143 | PermissionFlagsBits.ManageMessages 144 | ) 145 | ) { 146 | setTimeout(() => { 147 | message.delete(); 148 | }, 5_000); 149 | return; 150 | } else { 151 | setupData.numOnly = false; 152 | setupData.save(); 153 | message.channel.send( 154 | t("event.counting.numOnlyDisable", { 155 | lng, 156 | }) 157 | ); 158 | } 159 | } else return; 160 | } 161 | try { 162 | game.countResolve(); 163 | game.react(); 164 | game.updateDB(); 165 | game.sendMessage(); 166 | } catch (error) { 167 | console.log(error); 168 | } 169 | } 170 | } 171 | 172 | module.exports = CountingEvent; 173 | -------------------------------------------------------------------------------- /Events/Interaction/AutoComplete.js: -------------------------------------------------------------------------------- 1 | const Event = require("../../Structures/Classes/BaseEvent"); 2 | const { Events } = require("discord.js"); 3 | const { Logger } = require("../../Structures/Functions/index"); 4 | const logger = new Logger(); 5 | const { t } = require("i18next"); 6 | 7 | class AutoComplete extends Event { 8 | constructor(client) { 9 | super(client, { 10 | name: Events.InteractionCreate, 11 | }); 12 | } 13 | 14 | async execute(interaction) { 15 | const { client } = this; 16 | if (!interaction.isAutocomplete()) return; 17 | const autoComplete = client.autoComplete.get(interaction.commandName); 18 | if (!autoComplete) return; 19 | const lngCach = await client.redis.get(interaction.guildId + "lng"); 20 | let languageData; 21 | if (lngCach) { 22 | languageData = JSON.parse(lngCach); 23 | } else { 24 | languageData = await client.db.languageDatas.findOne({ 25 | guildId: interaction.guildId, 26 | }); 27 | if (!languageData && interaction.guildId !== null) { 28 | await client.db.languageDatas.create({ 29 | guildId: interaction.guildId, 30 | lng: "en", 31 | }); 32 | languageData = await client.db.languageDatas.findOne({ 33 | guildId: interaction.guildId, 34 | }); 35 | } 36 | await client.redis.set( 37 | interaction.guildId + "lng", 38 | JSON.stringify(languageData), 39 | "EX", 40 | 60 41 | ); 42 | } 43 | const lng = interaction.guildId == null ? "en" : languageData.lng; 44 | 45 | try { 46 | await autoComplete.execute(interaction, client, lng); 47 | } catch (error) { 48 | logger.error(error); 49 | } 50 | } 51 | } 52 | 53 | module.exports = AutoComplete; 54 | -------------------------------------------------------------------------------- /Events/Interaction/ButtonCreate.js: -------------------------------------------------------------------------------- 1 | const Event = require("../../Structures/Classes/BaseEvent"); 2 | const { Events } = require("discord.js"); 3 | const { Logger } = require("../../Structures/Functions/index"); 4 | const logger = new Logger(); 5 | const { t } = require("i18next"); 6 | 7 | class ButtonCreate extends Event { 8 | constructor(client) { 9 | super(client, { 10 | name: Events.InteractionCreate, 11 | }); 12 | } 13 | 14 | async execute(interaction) { 15 | const { client } = this; 16 | if (!interaction.isButton()) return; 17 | const button = client.buttons.get(interaction.customId); 18 | if (!button) return; 19 | const lngCach = await client.redis.get(interaction.guildId + "lng"); 20 | let languageData; 21 | if (lngCach) { 22 | languageData = JSON.parse(lngCach); 23 | } else { 24 | languageData = await client.db.languageDatas.findOne({ 25 | guildId: interaction.guildId, 26 | }); 27 | if (!languageData && interaction.guildId !== null) { 28 | await client.db.languageDatas.create({ 29 | guildId: interaction.guildId, 30 | lng: "en", 31 | }); 32 | languageData = await client.db.languageDatas.findOne({ 33 | guildId: interaction.guildId, 34 | }); 35 | } 36 | await client.redis.set( 37 | interaction.guildId + "lng", 38 | JSON.stringify(languageData), 39 | "EX", 40 | 60 41 | ); 42 | } 43 | const lng = interaction.guildId == null ? "en" : languageData.lng; 44 | 45 | try { 46 | await button.execute(interaction, client, lng); 47 | } catch (error) { 48 | logger.error(error); 49 | if (interaction.replied) { 50 | await interaction.editReply({ 51 | content: t("event.button.fail", { lng }), 52 | ephemeral: true, 53 | }); 54 | } else { 55 | await interaction.reply({ 56 | content: t("event.button.fail", { lng }), 57 | ephemeral: true, 58 | }); 59 | } 60 | } 61 | } 62 | } 63 | 64 | module.exports = ButtonCreate; 65 | -------------------------------------------------------------------------------- /Events/Interaction/InteractionCreate.js: -------------------------------------------------------------------------------- 1 | const Event = require("../../Structures/Classes/BaseEvent"); 2 | const { jsonFind, Logger } = require("../../Structures/Functions/index"); 3 | const { Events, InteractionType } = require("discord.js"); 4 | const logger = new Logger(); 5 | const { t } = require("i18next"); 6 | 7 | class InteractionCreate extends Event { 8 | constructor(client) { 9 | super(client, { 10 | name: Events.InteractionCreate, 11 | }); 12 | } 13 | 14 | async execute(interaction) { 15 | const { client } = this; 16 | if (interaction.type !== InteractionType.ApplicationCommand) return; 17 | 18 | const command = client.slashCommands.get(interaction.commandName); 19 | if (!command) return; 20 | const lngCach = await client.redis.get(interaction.guildId + "lng"); 21 | let languageData; 22 | if (lngCach) { 23 | languageData = JSON.parse(lngCach); 24 | } else { 25 | languageData = await client.db.languageDatas.findOne({ 26 | guildId: interaction.guildId, 27 | }); 28 | if (!languageData && interaction.guildId !== null) { 29 | await client.db.languageDatas.create({ 30 | guildId: interaction.guildId, 31 | lng: "en", 32 | }); 33 | languageData = await client.db.languageDatas.findOne({ 34 | guildId: interaction.guildId, 35 | }); 36 | } 37 | await client.redis.set( 38 | interaction.guildId + "lng", 39 | JSON.stringify(languageData), 40 | "EX", 41 | 60 42 | ); 43 | } 44 | const lng = interaction.guildId == null ? "en" : languageData.lng; 45 | 46 | if ( 47 | command.options?.devOnly && 48 | !jsonFind(interaction.user.id, client.config.developers) 49 | ) { 50 | return await interaction.reply({ 51 | content: t("event.command.devOnly", { 52 | lng, 53 | client: client.user.username, 54 | }), 55 | ephemeral: true, 56 | }); 57 | } 58 | 59 | if ( 60 | client.config.underDevelopment && 61 | !jsonFind(interaction.guild, client.config.devGuilds) && 62 | !jsonFind(interaction.guild, client.config.betaTestGuilds) 63 | ) { 64 | return await interaction.reply({ 65 | content: t("event.command.underDev", { lng }), 66 | ephemeral: true, 67 | }); 68 | } 69 | 70 | try { 71 | await command.execute(interaction, client, lng); 72 | } catch (error) { 73 | logger.error(error); 74 | if (interaction.replied) { 75 | await interaction.editReply({ 76 | content: t("event.command.fail", { lng }), 77 | ephemeral: true, 78 | }); 79 | } else { 80 | await interaction.reply({ 81 | content: t("event.command.fail", { lng }), 82 | ephemeral: true, 83 | }); 84 | } 85 | } 86 | } 87 | } 88 | 89 | module.exports = InteractionCreate; 90 | -------------------------------------------------------------------------------- /Events/Interaction/ModalCreate.js: -------------------------------------------------------------------------------- 1 | const Event = require("../../Structures/Classes/BaseEvent"); 2 | const { Events } = require("discord.js"); 3 | const { Logger } = require("../../Structures/Functions/index"); 4 | const logger = new Logger(); 5 | const { t } = require("i18next"); 6 | 7 | class ModalCreate extends Event { 8 | constructor(client) { 9 | super(client, { 10 | name: Events.InteractionCreate, 11 | }); 12 | } 13 | 14 | async execute(interaction) { 15 | const { client } = this; 16 | if (!interaction.isModalSubmit()) return; 17 | const modal = client.modals.get(interaction.customId); 18 | if (!modal) return; 19 | const lngCach = await client.redis.get(interaction.guildId + "lng"); 20 | let languageData; 21 | if (lngCach) { 22 | languageData = JSON.parse(lngCach); 23 | } else { 24 | languageData = await client.db.languageDatas.findOne({ 25 | guildId: interaction.guildId, 26 | }); 27 | if (!languageData && interaction.guildId !== null) { 28 | await client.db.languageDatas.create({ 29 | guildId: interaction.guildId, 30 | lng: "en", 31 | }); 32 | languageData = await client.db.languageDatas.findOne({ 33 | guildId: interaction.guildId, 34 | }); 35 | } 36 | await client.redis.set( 37 | interaction.guildId + "lng", 38 | JSON.stringify(languageData), 39 | "EX", 40 | 60 41 | ); 42 | } 43 | const lng = interaction.guildId == null ? "en" : languageData.lng; 44 | 45 | try { 46 | await modal.execute(interaction, client, lng); 47 | } catch (error) { 48 | logger.error(error); 49 | if (interaction.replied) { 50 | await interaction.editReply({ 51 | content: t("event.modal.fail", { lng }), 52 | ephemeral: true, 53 | }); 54 | } else { 55 | await interaction.reply({ 56 | content: t("event.modal.fail", { lng }), 57 | ephemeral: true, 58 | }); 59 | } 60 | } 61 | } 62 | } 63 | 64 | module.exports = ModalCreate; 65 | -------------------------------------------------------------------------------- /Events/Log/InteractionLog.js: -------------------------------------------------------------------------------- 1 | const Event = require("../../Structures/Classes/BaseEvent"); 2 | const { 3 | Events, 4 | CommandInteraction, 5 | InteractionType, 6 | EmbedBuilder, 7 | Colors, 8 | } = require("discord.js"); 9 | 10 | class InteractionLog extends Event { 11 | constructor(client) { 12 | super(client, { 13 | name: Events.InteractionCreate, 14 | }); 15 | } 16 | 17 | async execute(interaction) { 18 | const { client } = this; 19 | if ( 20 | interaction instanceof CommandInteraction && 21 | interaction.type === InteractionType.ApplicationCommand 22 | ) { 23 | const command = client.slashCommands.get(interaction.commandName); 24 | if (!command) return; 25 | 26 | let botData = await client.db.botDatas.findOne({ 27 | password: "jasonmidul", 28 | }); 29 | 30 | if (!botData) { 31 | await client.db.botDatas.create({ password: "jasonmidul" }); 32 | botData = await client.db.botDatas.findOne({ password: "jasonmidul" }); 33 | } 34 | const cmdsUsed = botData.cmdUsed; 35 | botData.cmdUsed += 1; 36 | botData.save(); 37 | 38 | const channel = await client.channels.cache.get(client.config.logChannel); 39 | const server = interaction.guild?.name || "user"; 40 | const user = interaction.user.username; 41 | const userId = interaction.user.id; 42 | 43 | const embed = new EmbedBuilder() 44 | .setDescription(`${cmdsUsed}th command`) 45 | .setColor(Colors.Green) 46 | .addFields({ name: "User", value: `${user} \`${userId}\`` }) 47 | .addFields({ name: "Command", value: `${interaction}` }) 48 | .addFields({ 49 | name: "server id", 50 | value: `\`${interaction.guild?.id || "NuN"}\``, 51 | }) 52 | .setFooter({ text: server }) 53 | .setTimestamp(); 54 | 55 | await channel.send({ embeds: [embed] }); 56 | } 57 | } 58 | } 59 | 60 | module.exports = InteractionLog; 61 | -------------------------------------------------------------------------------- /Events/Topgg/VoteReward.js: -------------------------------------------------------------------------------- 1 | const Event = require("../../Structures/Classes/BaseEvent"); 2 | const { voteLog } = require("../../Structures/Functions/index"); 3 | const { Logger } = require("../../Structures/Functions/index"); 4 | 5 | const logger = new Logger(); 6 | const express = require("express"); 7 | const Topgg = require("@top-gg/sdk"); 8 | 9 | const app = express(); 10 | const webhook = new Topgg.Webhook("vote"); 11 | 12 | const { Events, EmbedBuilder, Colors } = require("discord.js"); 13 | 14 | class VoteReward extends Event { 15 | constructor(client) { 16 | super(client, { 17 | name: Events.ClientReady, 18 | }); 19 | } 20 | 21 | async execute(client) { 22 | app.post( 23 | "/vote", 24 | webhook.listener(async (vote) => { 25 | let userData = await client.db.userDatas.findOne({ 26 | userId: vote.user, 27 | }); 28 | 29 | let embed = new EmbedBuilder(); 30 | if (!userData) { 31 | client.db.userDatas.create({ 32 | userId: vote.user, 33 | }); 34 | userData = await client.db.userDatas.findOne({ 35 | userId: vote.user, 36 | }); 37 | } 38 | if (userData.saveSlot > userData.saves) { 39 | userData.saves += 1; 40 | userData.vote.count += 1; 41 | userData.vote.time = new Date(); 42 | userData.save(); 43 | 44 | embed 45 | .setTitle("🎁 You just voted me!") 46 | .setDescription( 47 | `**You claimed** \`1\` **save as vote reward!** \n> You can vote [here](${client.config.voteUrl}) in every \`12h\`. \n> Now you have **${userData.saves}/${userData.saveSlot}** saves.\n> Use \`/profile\` for more information.` 48 | ) 49 | .setColor(Colors.DarkGreen) 50 | .setFooter({ text: `Thanks for voting me! ❤️` }) 51 | .setTimestamp(); 52 | } else { 53 | userData.vote.count += 1; 54 | userData.vote.time = new Date(); 55 | userData.save(); 56 | 57 | embed 58 | .setTitle("🎁 You just voted me!") 59 | .setDescription( 60 | `**You vote slot is full. You need to upgrade your profile for increase your saves slote.**\n> You claimed noting as vote reward! \n> You can vote [here](https://top.gg/bot/${client.config.clientId}) in every \`12h\`. \n> Now you have **${userData.saves}/${userData.saveSlot}** saves.\n> Use \`/profile\` for more information.` 61 | ) 62 | .setColor(Colors.DarkGreen) 63 | .setFooter({ text: `Thanks for voting me! ❤️` }) 64 | .setTimestamp(); 65 | } 66 | const voteUser = await client.users.fetch(vote.user); 67 | voteLog(client, `${vote.user}`, `${voteUser.username}`); 68 | 69 | if (voteUser) { 70 | try { 71 | await voteUser.send({ embeds: [embed] }); 72 | 73 | logger.info(`Dm sent to - ` + vote.user); 74 | } catch (err) { 75 | logger.info(`Cant dm - ` + vote.user + err); 76 | } 77 | } 78 | }) 79 | ); 80 | 81 | app.listen(6017); 82 | } 83 | } 84 | 85 | module.exports = VoteReward; 86 | -------------------------------------------------------------------------------- /Interactions/Components/Buttons/Delete.js: -------------------------------------------------------------------------------- 1 | const Component = require("../../../Structures/Classes/BaseComponent"); 2 | class Delete extends Component { 3 | constructor(client) { 4 | super(client, { 5 | id: "delete", 6 | }); 7 | } 8 | /** 9 | * 10 | * @param {import("discord.js").ButtonInteraction} interaction 11 | */ 12 | async execute(interaction) { 13 | interaction.channel.messages.delete(interaction.message.id); 14 | } 15 | } 16 | module.exports = Delete; 17 | -------------------------------------------------------------------------------- /Interactions/Components/Modals/DeleteConfirm.js: -------------------------------------------------------------------------------- 1 | const Component = require("../../../Structures/Classes/BaseComponent"); 2 | const { EmbedBuilder, Colors } = require("discord.js"); 3 | 4 | class Redeem extends Component { 5 | constructor(client) { 6 | super(client, { 7 | id: "setup-delete", 8 | }); 9 | } 10 | /** 11 | * 12 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 13 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 14 | * @param {string} lng 15 | */ 16 | async execute(interaction, client, lng) { 17 | const input = interaction.fields.getTextInputValue("confirm"); 18 | if (input !== "confirm") 19 | return await interaction.reply({ 20 | content: "> Setup deletion failed. You entered rong value.", 21 | }); 22 | const setupData = await client.db.setupDatas.findOne({ 23 | guildId: interaction.guild.id, 24 | }); 25 | const embed = new EmbedBuilder() 26 | .setColor(Colors.DarkGreen) 27 | .setDescription( 28 | `\`✅\` The counting channel **(**<#${setupData.setupChannel}>**)** has removed from this guild.` 29 | ); 30 | await client.db.setupDatas.findOneAndDelete({ 31 | guildId: interaction.guild.id, 32 | }); 33 | await client.redis.del(interaction.guildId + "setup"); 34 | 35 | const updateGame = await client.db.gameDatas.findOneAndUpdate( 36 | { guildId: interaction.guildId }, 37 | { 38 | count: 0, 39 | lastCounter: "none", 40 | }, 41 | { upsert: true, new: true } 42 | ); 43 | await client.redis.set( 44 | this.guildId + "game", 45 | JSON.stringify(updateGame), 46 | "EX", 47 | 60 48 | ); 49 | await interaction.reply({ embeds: [embed] }); 50 | } 51 | } 52 | 53 | module.exports = Redeem; 54 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/Admin/Language.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js"); 3 | const { t } = require("i18next"); 4 | 5 | class Language extends Command { 6 | constructor(client, dir) { 7 | super(client, dir, { 8 | data: new SlashCommandBuilder() 9 | .setName("language") 10 | .setDescription("Set a language for this server.") 11 | .setDMPermission(false) 12 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) 13 | .addStringOption((option) => 14 | option 15 | .setName("language") 16 | .setDescription("Select a language.") 17 | .setRequired(true) 18 | .addChoices([ 19 | { name: "English", value: "en" }, 20 | { name: "বাংলা", value: "bn" }, 21 | { name: "Português", value: "pt" }, 22 | ]) 23 | ), 24 | options: { 25 | category: "Admin", 26 | }, 27 | }); 28 | } 29 | /** 30 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 31 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 32 | */ 33 | async execute(interaction, client) { 34 | const lng = interaction.options.getString("language"); 35 | 36 | const languageData = await client.db.languageDatas.findOneAndUpdate( 37 | { guildId: interaction.guildId }, 38 | { lng: lng }, 39 | { upsert: true, new: true } 40 | ); 41 | 42 | await client.redis.set( 43 | interaction.guildId + "lng", 44 | JSON.stringify(languageData), 45 | "EX", 46 | 60 47 | ); 48 | 49 | interaction.reply({ 50 | content: t("command:language.success", { 51 | lng: lng, 52 | data: lng == "en" ? "English" : lng == "bn" ? "বাংলা" : "Português", 53 | user: interaction.user.id, 54 | }), 55 | }); 56 | } 57 | } 58 | 59 | module.exports = Language; 60 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/Admin/Move.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { 3 | SlashCommandBuilder, 4 | PermissionFlagsBits, 5 | ChannelType, 6 | } = require("discord.js"); 7 | const { t } = require("i18next"); 8 | 9 | class Toggle extends Command { 10 | constructor(client, dir) { 11 | super(client, dir, { 12 | data: new SlashCommandBuilder() 13 | .setName("move") 14 | .setDescription("To move the counting game in another channel.") 15 | .setDMPermission(false) 16 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) 17 | .addChannelOption((option) => 18 | option 19 | .setName("channel") 20 | .setDescription("Which channel you want to setup counting game.") 21 | .setRequired(true) 22 | .addChannelTypes(ChannelType.GuildText) 23 | ), 24 | options: { 25 | category: "Admin", 26 | }, 27 | }); 28 | } 29 | /** 30 | * 31 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 32 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 33 | * @param {string} lng 34 | */ 35 | async execute(interaction, client, lng) { 36 | const channel = interaction.options.getChannel("channel"); 37 | let setupData = await client.db.setupDatas.findOne({ 38 | guildId: interaction.guild.id, 39 | }); 40 | if (!setupData) 41 | return await interaction.reply({ 42 | content: 43 | "> Counting channel not available in this server. Use `/setup create` to setup count game.", 44 | ephemeral: true, 45 | }); 46 | setupData = await client.db.setupDatas.findOneAndUpdate( 47 | { guildId: interaction.guildId }, 48 | { setupChannel: channel.id }, 49 | { upsert: true, new: true } 50 | ); 51 | await client.redis.set( 52 | interaction.guildId + "setup", 53 | JSON.stringify(setupData), 54 | "EX", 55 | 60 56 | ); 57 | 58 | await interaction.reply({ 59 | content: `> Counting channel has successfully moved to <#${channel.id}>.`, 60 | ephemeral: true, 61 | }); 62 | await channel.send( 63 | `✨ **This channel has been updated as a counting channel!**` 64 | ); 65 | channel.setTopic(client.config.cTopic); 66 | } 67 | } 68 | 69 | module.exports = Toggle; 70 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/Admin/Setup.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { 3 | SlashCommandBuilder, 4 | EmbedBuilder, 5 | Colors, 6 | ChannelType, 7 | PermissionFlagsBits, 8 | ModalBuilder, 9 | TextInputBuilder, 10 | ActionRowBuilder, 11 | TextInputStyle, 12 | } = require("discord.js"); 13 | 14 | class Setup extends Command { 15 | constructor(client, dir) { 16 | super(client, dir, { 17 | data: new SlashCommandBuilder() 18 | .setName("setup") 19 | .setDMPermission(false) 20 | .setDescription("Setup create, delete or update.") 21 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) 22 | .addSubcommand((subCommand) => 23 | subCommand 24 | .setName("create") 25 | .setDescription("To setup a counting setup.") 26 | .addChannelOption((option) => 27 | option 28 | .setName("channel") 29 | .setDescription( 30 | "Which channel you want to setup counting game." 31 | ) 32 | .setRequired(true) 33 | .addChannelTypes(ChannelType.GuildText) 34 | ) 35 | ) 36 | .addSubcommand((subCommand) => 37 | subCommand 38 | .setName("delete") 39 | .setDescription("To delete a counting setup.") 40 | ), 41 | options: { 42 | category: "Admin", 43 | }, 44 | }); 45 | } 46 | /** 47 | * 48 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 49 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 50 | * @param {string} lng 51 | */ 52 | async execute(interaction, client, lng) { 53 | const subCmd = interaction.options.getSubcommand(); 54 | const channel = interaction.options.getChannel("channel"); 55 | const setupData = await client.db.setupDatas.findOne({ 56 | guildId: interaction.guild.id, 57 | }); 58 | const gameData = await client.db.gameDatas.findOne({ 59 | guildId: interaction.guild.id, 60 | }); 61 | const embed = new EmbedBuilder().setColor(Colors.DarkGreen); 62 | 63 | switch (subCmd) { 64 | case "create": 65 | if (setupData) { 66 | embed.setDescription( 67 | `\`❌\` The counting channel has already setuped in <#${setupData.setupChannel}>. Use \`/setup update\` for move or update setup settings.` 68 | ); 69 | return interaction.reply({ embeds: [embed] }); 70 | } else { 71 | client.db.setupDatas.create({ 72 | guildId: interaction.guild.id, 73 | setupChannel: channel.id, 74 | }); 75 | if (!gameData) { 76 | client.db.gameDatas.create({ 77 | guildId: interaction.guild.id, 78 | name: interaction.guild.name, 79 | }); 80 | } 81 | embed.setDescription( 82 | `\`✅\` **The counting channel has been setuped in <#${channel.id}>.**` 83 | ); 84 | } 85 | await interaction.reply({ embeds: [embed] }); 86 | await channel.send( 87 | `✨ **This channel has been setuped as a counting channel!**` 88 | ); 89 | channel.setTopic(client.config.cTopic); 90 | break; 91 | case "delete": 92 | if (!setupData) { 93 | embed.setDescription( 94 | `\`❌\` Counting channel is not setuped in this server.` 95 | ); 96 | return interaction.reply({ embeds: [embed] }); 97 | } else { 98 | const modal = new ModalBuilder() 99 | .setCustomId("setup-delete") 100 | .setTitle("confirm?"); 101 | 102 | const codeResiver = new TextInputBuilder() 103 | .setCustomId("confirm") 104 | .setLabel("Delete counting setup? Use /move instead.") 105 | .setStyle(TextInputStyle.Short) 106 | .setPlaceholder("type 'confirm' to delete") 107 | .setRequired(true); 108 | const actionRow = new ActionRowBuilder().addComponents(codeResiver); 109 | modal.addComponents(actionRow); 110 | await interaction.showModal(modal); 111 | } 112 | break; 113 | default: 114 | break; 115 | } 116 | } 117 | } 118 | 119 | module.exports = Setup; 120 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/Admin/Toggle.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js"); 3 | const { t } = require("i18next"); 4 | 5 | class Toggle extends Command { 6 | constructor(client, dir) { 7 | super(client, dir, { 8 | data: new SlashCommandBuilder() 9 | .setName("toggle") 10 | .setDescription("🔑 Toggle different setting for counting game.") 11 | .setDMPermission(false) 12 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) 13 | .addStringOption((option) => 14 | option 15 | .setName("setting") 16 | .setDescription("The setting to toggle.") 17 | .setRequired(true) 18 | .addChoices( 19 | { 20 | name: "Math Calculate - if true the bot game will calculate maths in counting channel.", 21 | value: "math", 22 | }, 23 | { 24 | name: "Number Only - if true bot will delete all normal messages from counting channel.", 25 | value: "numOnly", 26 | } 27 | ) 28 | ) 29 | .addBooleanOption((option) => 30 | option 31 | .setName("status") 32 | .setDescription("Whether to toggle true or false.") 33 | 34 | .setRequired(true) 35 | ), 36 | options: { 37 | category: "Admin", 38 | }, 39 | }); 40 | } 41 | /** 42 | * 43 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 44 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 45 | * @param {string} lng 46 | */ 47 | async execute(interaction, client, lng) { 48 | const setting = interaction.options.getString("setting"); 49 | const status = interaction.options.getBoolean("status"); 50 | const setupData = await client.db.setupDatas.findOne({ 51 | guildId: interaction.guild.id, 52 | }); 53 | if (!setupData) 54 | return await interaction.reply({ 55 | content: 56 | "> Counting channel not available in this server. Use `/setup create` to setup count game.", 57 | ephemeral: true, 58 | }); 59 | let updateSetup; 60 | switch (setting) { 61 | case "math": 62 | updateSetup = await client.db.setupDatas.findOneAndUpdate( 63 | { guildId: interaction.guildId }, 64 | { math: status }, 65 | { upsert: true, new: true } 66 | ); 67 | await client.redis.set( 68 | interaction.guildId + "setup", 69 | JSON.stringify(updateSetup), 70 | "EX", 71 | 60 72 | ); 73 | 74 | await interaction.reply({ 75 | content: `> Math Calculate setting has successfully toggled to **${status}**.`, 76 | ephemeral: true, 77 | }); 78 | break; 79 | case "numOnly": 80 | updateSetup = await client.db.setupDatas.findOneAndUpdate( 81 | { guildId: interaction.guildId }, 82 | { numOnly: status }, 83 | { upsert: true, new: true } 84 | ); 85 | await client.redis.set( 86 | interaction.guildId + "setup", 87 | JSON.stringify(updateSetup), 88 | "EX", 89 | 60 90 | ); 91 | await interaction.reply({ 92 | content: `> Number Only setting has successfully toggled to **${status}**.`, 93 | ephemeral: true, 94 | }); 95 | break; 96 | default: 97 | break; 98 | } 99 | } 100 | } 101 | 102 | module.exports = Toggle; 103 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/Dev/Eval.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { 3 | SlashCommandBuilder, 4 | AttachmentBuilder, 5 | PermissionFlagsBits, 6 | } = require("discord.js"); 7 | 8 | class Eval extends Command { 9 | constructor(client, dir) { 10 | super(client, dir, { 11 | data: new SlashCommandBuilder() 12 | .setName("eval") 13 | .setDescription("Eval a code.") 14 | .setDMPermission(false) 15 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) 16 | .addStringOption((option) => 17 | option 18 | .setName("code") 19 | .setDescription("The code you want to eval.") 20 | .setRequired(true) 21 | ), 22 | options: { 23 | devOnly: true, 24 | }, 25 | }); 26 | } 27 | async execute(interaction, client) { 28 | try { 29 | const code = interaction.options.getString("code"); 30 | 31 | const evaled = eval(code); 32 | 33 | async function clean(client, text) { 34 | if (text && text.constructor.name == "Promise") text = await text; 35 | 36 | if (typeof text !== "string") 37 | text = require("util").inspect(text, { depth: 1 }); 38 | 39 | text = text 40 | .replace(/`/g, "`" + String.fromCharCode(8203)) 41 | .replace(/@/g, "@" + String.fromCharCode(8203)); 42 | text = text.replaceAll(client.token, "[REDACTED]"); 43 | return text; 44 | } 45 | 46 | const reply = await clean(client, evaled); 47 | 48 | if (reply?.length > 1980) { 49 | const buffer = Buffer.from(reply, "utf8"); 50 | const txtFile = new AttachmentBuilder(buffer, { 51 | name: `${interaction.user.username}_response.txt`, 52 | }); 53 | 54 | await interaction.reply({ 55 | files: [txtFile], 56 | ephemeral: true, 57 | }); 58 | } else { 59 | interaction.reply({ 60 | content: `\`\`\`js\n${reply}\n\`\`\``, 61 | ephemeral: true, 62 | }); 63 | } 64 | } catch (err) { 65 | interaction.reply({ 66 | content: `\`ERROR\` \n\`\`\`xl\n${err}\n\`\`\``, 67 | ephemeral: true, 68 | }); 69 | } 70 | } 71 | } 72 | 73 | module.exports = Eval; 74 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/Dev/GuildLeave.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { 3 | SlashCommandBuilder, 4 | AttachmentBuilder, 5 | PermissionFlagsBits, 6 | } = require("discord.js"); 7 | 8 | class GuildLeave extends Command { 9 | constructor(client, dir) { 10 | super(client, dir, { 11 | data: new SlashCommandBuilder() 12 | .setName("gleave") 13 | .setDescription("Leaves a server.") 14 | .setDMPermission(false) 15 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) 16 | .addStringOption((option) => 17 | option 18 | .setName("id") 19 | .setDescription( 20 | "enter guild id to leave (type `list` for all guilds)." 21 | ) 22 | .setRequired(true) 23 | ), 24 | options: { 25 | devOnly: true, 26 | }, 27 | }); 28 | } 29 | async execute(interaction, client) { 30 | try { 31 | const id = interaction.options.getString("id"); 32 | 33 | if (id.toLowerCase() === "list") { 34 | client.guilds.cache.forEach((guild) => { 35 | console.log(`${guild.name} | ${guild.id}`); 36 | }); 37 | const guild = client.guilds.cache.map( 38 | (guild) => ` ${guild.name} | ${guild.id}` 39 | ); 40 | try { 41 | return interaction.reply({ 42 | content: `Guilds:\n\`${guild}\``, 43 | ephemeral: true, 44 | }); 45 | } catch { 46 | return interaction.reply({ 47 | content: `check console for list of guilds`, 48 | ephemeral: true, 49 | }); 50 | } 51 | } 52 | 53 | const guild = client.guilds.cache.get(id); 54 | 55 | if (!guild) { 56 | return interaction.reply({ 57 | content: `\`${id}\` is not a valid guild id`, 58 | ephemeral: true, 59 | }); 60 | } 61 | 62 | await guild 63 | .leave() 64 | .then((c) => console.log(`Left guild ${id}`)) 65 | .catch((err) => { 66 | console.log(err); 67 | }); 68 | return interaction.reply({ 69 | content: `Left guild \`${id}\``, 70 | ephemeral: true, 71 | }); 72 | } catch (error) { 73 | console.log(`there was an error trying to leave guild ${id}`, error); 74 | } 75 | } 76 | } 77 | 78 | module.exports = GuildLeave; 79 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/Dev/Reload.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { 3 | CommandHandler, 4 | } = require("../../../Structures/Handlers/CommandHandler"); 5 | const { 6 | ComponentHandler, 7 | } = require("../../../Structures/Handlers/ComponentHandler"); 8 | const { EventHandler } = require("../../../Structures/Handlers/EventHandler"); 9 | const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js"); 10 | const { Logger } = require("../../../Structures/Functions/index"); 11 | const logger = new Logger(); 12 | 13 | class Reload extends Command { 14 | constructor(client, dir) { 15 | super(client, dir, { 16 | data: new SlashCommandBuilder() 17 | .setName("reload") 18 | .setDescription("Reload commands/events!") 19 | .setDMPermission(false) 20 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) 21 | .addSubcommand((subCommand) => 22 | subCommand.setName("events").setDescription("Reload all events.") 23 | ) 24 | .addSubcommand((subCommand) => 25 | subCommand 26 | .setName("component") 27 | .setDescription("Reload all components.") 28 | ) 29 | .addSubcommand((subCommand) => 30 | subCommand 31 | .setName("commands") 32 | .setDescription("Reload/register all slash commands.") 33 | .addStringOption((option) => 34 | option 35 | .setName("deploy-slash") 36 | .setDescription("By default it's (false)") 37 | .setRequired(false) 38 | .addChoices( 39 | { name: "true", value: "true" }, 40 | { name: "false", value: "false" } 41 | ) 42 | ) 43 | ), 44 | options: { 45 | devOnly: true, 46 | }, 47 | }); 48 | } 49 | async execute(interaction, client) { 50 | const subCmd = interaction.options.getSubcommand(); 51 | switch (subCmd) { 52 | case "commands": 53 | try { 54 | const isDeploySlash = 55 | interaction.options.getString("deploy-slash") || false; 56 | const { loadCommands } = new CommandHandler(); 57 | await loadCommands(client, isDeploySlash); 58 | interaction.reply({ 59 | content: `All commands has been reloaded. \nAnd deploy slash was \`${isDeploySlash}\``, 60 | ephemeral: true, 61 | }); 62 | } catch (error) { 63 | logger.error(error); 64 | } 65 | break; 66 | case "component": 67 | try { 68 | const { loadComponents } = new ComponentHandler(); 69 | await loadComponents(client); 70 | interaction.reply({ 71 | content: `All components has been reloaded.`, 72 | ephemeral: true, 73 | }); 74 | } catch (error) { 75 | logger.error(error); 76 | } 77 | break; 78 | case "events": 79 | try { 80 | for (const [key, value] of client.events) 81 | client.removeListener(value.name, value.execute); 82 | const { loadEvents } = new EventHandler(); 83 | await loadEvents(client); 84 | interaction.reply({ 85 | content: `All events has been reloaded.`, 86 | ephemeral: true, 87 | }); 88 | } catch (error) { 89 | logger.error(error); 90 | } 91 | default: 92 | break; 93 | } 94 | } 95 | } 96 | 97 | module.exports = Reload; 98 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/Info/Help.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { SlashCommandBuilder, Colors, EmbedBuilder } = require("discord.js"); 3 | const { PaginationEmbed } = require("../../../Structures/Functions/index"); 4 | 5 | class Help extends Command { 6 | constructor(client, dir) { 7 | super(client, dir, { 8 | data: new SlashCommandBuilder() 9 | .setName("help") 10 | .setDescription("help command"), 11 | options: { 12 | category: "Info", 13 | }, 14 | }); 15 | } 16 | 17 | /** 18 | * 19 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 20 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 21 | */ 22 | async execute(interaction, client) { 23 | let userCmd = []; 24 | let adminCmd = []; 25 | let infoCmd = []; 26 | let othersCmd = []; 27 | 28 | const cmdPush = (category, subCmd, command) => { 29 | try { 30 | switch (category.category) { 31 | case "User": 32 | if (subCmd.length !== 0) userCmd.push(...subCmd); 33 | else 34 | userCmd.push({ 35 | name: ``, 36 | value: command.description, 37 | inline: true, 38 | }); 39 | break; 40 | case "Info": 41 | if (subCmd.length !== 0) infoCmd.push(...subCmd); 42 | else 43 | infoCmd.push({ 44 | name: ``, 45 | value: command.description, 46 | inline: true, 47 | }); 48 | break; 49 | case "Admin": 50 | if (subCmd.length !== 0) adminCmd.push(...subCmd); 51 | else 52 | adminCmd.push({ 53 | name: ``, 54 | value: command.description, 55 | inline: true, 56 | }); 57 | break; 58 | default: 59 | if (subCmd.length !== 0) othersCmd.push(...subCmd); 60 | else 61 | othersCmd.push({ 62 | name: ``, 63 | value: command.description, 64 | inline: true, 65 | }); 66 | break; 67 | } 68 | } catch (error) { 69 | console.log(error); 70 | } 71 | }; 72 | 73 | await client.application.commands 74 | .fetch() 75 | .then((commands) => { 76 | commands.forEach((command) => { 77 | let subCmd = []; 78 | command.options.forEach((option) => { 79 | if (option.type == 1) { 80 | subCmd.push({ 81 | name: ``, 82 | value: option.description, 83 | inline: true, 84 | }); 85 | } 86 | }); 87 | const ctg = client.slashCommands.get(command.name); 88 | cmdPush(ctg, subCmd, command); 89 | }); 90 | }) 91 | .catch((err) => { 92 | console.error("Error fetching commands:", err); 93 | }); 94 | const embeds = [ 95 | new EmbedBuilder() 96 | .setTitle("User Commands") 97 | .addFields(userCmd) 98 | .setColor(Colors.DarkGreen), 99 | 100 | new EmbedBuilder() 101 | .setTitle("Info Commands") 102 | .addFields(infoCmd) 103 | .setColor(Colors.DarkGreen), 104 | 105 | new EmbedBuilder() 106 | .setTitle("Admin Commands") 107 | .addFields(adminCmd) 108 | .setColor(Colors.DarkGreen), 109 | 110 | new EmbedBuilder() 111 | .setTitle("Other Commands") 112 | .addFields( 113 | othersCmd.length !== 0 114 | ? othersCmd 115 | : { 116 | name: "⠀", 117 | value: "No command available in this category.", 118 | } 119 | ) 120 | .setColor(Colors.DarkGreen), 121 | ]; 122 | 123 | await PaginationEmbed(interaction, embeds); 124 | } 125 | } 126 | 127 | module.exports = Help; 128 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/Info/Ping.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { SlashCommandBuilder, EmbedBuilder, Colors } = require("discord.js"); 3 | const { t } = require("i18next"); 4 | 5 | class Ping extends Command { 6 | constructor(client, dir) { 7 | super(client, dir, { 8 | data: new SlashCommandBuilder() 9 | .setName("ping") 10 | .setDescription("🏓 To check bot ping!") 11 | .setDMPermission(false), 12 | options: { 13 | category: "Info", 14 | }, 15 | }); 16 | } 17 | /** 18 | * 19 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 20 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 21 | * @param {string} lng 22 | */ 23 | async execute(interaction, client, lng) { 24 | await interaction.reply(t("command:ping.reply", { lng })); 25 | const msg = await interaction.fetchReply(); 26 | const ping = Math.floor( 27 | msg.createdTimestamp - interaction.createdTimestamp 28 | ); 29 | const embed = new EmbedBuilder() 30 | .setColor( 31 | ping < 20 ? Colors.Green : ping < 40 ? Colors.Yellow : Colors.Red 32 | ) 33 | .setDescription( 34 | t("command:ping.embed.description", { 35 | lng, 36 | client: client.user.username, 37 | ping, 38 | apiPing: client.ws.ping, 39 | uptime: parseInt(`${client.readyTimestamp / 1000}`), 40 | }) 41 | ); 42 | interaction.editReply({ embeds: [embed], content: "" }); 43 | } 44 | } 45 | 46 | module.exports = Ping; 47 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/User/Leaderboard.js: -------------------------------------------------------------------------------- 1 | const { SlashCommandBuilder, EmbedBuilder, Colors } = require("discord.js"); 2 | const Command = require("../../../Structures/Classes/BaseCommand"); 3 | 4 | class leaderboard extends Command { 5 | constructor(client, dir) { 6 | super(client, dir, { 7 | data: new SlashCommandBuilder() 8 | .setName("leaderboard") 9 | .setDMPermission(false) 10 | .setDescription("View various leaderboards.") 11 | .addSubcommand((subCommand) => { 12 | return subCommand 13 | .setName("server") 14 | .setDescription("View the top ten server score.") 15 | .addNumberOption((num) => { 16 | return num 17 | .setName("page") 18 | .setRequired(false) 19 | .setDescription( 20 | "The page on the leaderboard you would like to view." 21 | ); 22 | }); 23 | }) 24 | .addSubcommand((subCommand) => { 25 | return subCommand 26 | .setName("user") 27 | .setDescription("View the top ten user score.") 28 | .addNumberOption((num) => { 29 | return num 30 | .setName("page") 31 | .setRequired(false) 32 | .setDescription( 33 | "The page on the leaderboard you would like to view." 34 | ); 35 | }); 36 | }), 37 | options: { 38 | category: "User", 39 | }, 40 | }); 41 | } 42 | /** 43 | * 44 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 45 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 46 | * @param {string} lng 47 | */ 48 | async execute(interaction, client, lng) { 49 | const subCmd = interaction.options.getSubcommand(); 50 | const page = interaction.options.getNumber("page") || 1; 51 | const pageNum = 10 * page - 10; 52 | let index = pageNum * 10; 53 | let dis = ""; 54 | 55 | const embed = new EmbedBuilder().setColor(Colors.DarkGreen).setTimestamp(); 56 | 57 | switch (subCmd) { 58 | case "server": 59 | const gameDatas = await client.db.gameDatas.find().sort({ count: -1 }); 60 | const guildScore = await client.db.gameDatas.findOne({ 61 | guildId: interaction.guild.id, 62 | }); 63 | const setupExist = await client.db.setupDatas.findOne({ 64 | guildId: interaction.guild.id, 65 | }); 66 | 67 | if (guildScore && setupExist) { 68 | const rank = gameDatas.findIndex( 69 | (b) => b.guildId === interaction.guild.id 70 | ); 71 | 72 | dis = `${interaction.guild.name} on at **#${ 73 | rank + 1 74 | }** in the leaderboard with **${guildScore.count}** score! \n`; 75 | } else if (guildScore && !setupExist) { 76 | const rank = gameDatas.findIndex( 77 | (b) => b.guildId === interaction.guild.id 78 | ); 79 | 80 | dis = `${interaction.guild.name} on at **#${ 81 | rank + 1 82 | }** in the leaderboard with **${ 83 | guildScore.count 84 | }** score but counting channel is not setuped in this guild. \n`; 85 | } else { 86 | dis = `The counting channel is not setuped in this guild. Use \`setup create\` to get a position on leaderboard. \n`; 87 | } 88 | 89 | if (gameDatas.length < pageNum) { 90 | return await interaction.reply({ 91 | content: `❌ Unable to find page no **${page}**.`, 92 | }); 93 | } 94 | if (gameDatas.length >= 11) { 95 | embed.setFooter({ 96 | text: `page ${page} of ${Math.ceil(gameDatas.length / 10)}`, 97 | }); 98 | } 99 | 100 | for (const i of gameDatas.splice(pageNum, 10)) { 101 | dis = 102 | dis + 103 | `\n> \`${ 104 | index + 1 === 1 105 | ? "🥇" 106 | : index + 1 === 2 107 | ? "🥈" 108 | : index + 1 === 3 109 | ? "🥉" 110 | : `#${index + 1}` 111 | }\`` + 112 | ` ${i.name}, **${i.count}**`; 113 | index += 1; 114 | } 115 | embed.setDescription(dis).setTitle("🏆 Server Leaderboard"); 116 | 117 | await interaction.reply({ embeds: [embed] }); 118 | 119 | break; 120 | case "user": 121 | const userDatas = await client.db.userDatas.find().sort({ score: -1 }); 122 | const userScore = await client.db.userDatas.findOne({ 123 | userId: interaction.user.id, 124 | }); 125 | 126 | if (userScore) { 127 | const rank = userDatas.findIndex( 128 | (b) => b.userId === interaction.user.id 129 | ); 130 | 131 | dis = `You are on at **#${rank + 1}** in the leaderboard with **${ 132 | userScore.score 133 | }** score! \n`; 134 | } else { 135 | dis = ``; 136 | } 137 | 138 | if (userDatas.length < pageNum) { 139 | return await interaction.reply({ 140 | content: `❌ Unable to find page no **${page}**.`, 141 | }); 142 | } 143 | if (userDatas.length >= 11) { 144 | embed.setFooter({ 145 | text: `page ${page} of ${Math.ceil(userDatas.length / 10)}`, 146 | }); 147 | } 148 | for (const i of userDatas.splice(pageNum, 10)) { 149 | dis = 150 | dis + 151 | `\n> \`${ 152 | index + 1 === 1 153 | ? "🥇" 154 | : index + 1 === 2 155 | ? "🥈" 156 | : index + 1 === 3 157 | ? "🥉" 158 | : `#${index + 1}` 159 | }\`` + 160 | ` ${i.name}, **${i.score}**`; 161 | index += 1; 162 | } 163 | embed.setDescription(dis).setTitle("🏆 User Leaderboard"); 164 | 165 | await interaction.reply({ embeds: [embed] }); 166 | break; 167 | 168 | default: 169 | break; 170 | } 171 | } 172 | } 173 | 174 | module.exports = leaderboard; 175 | // async execute(interaction, client) { 176 | // const sub = interaction.options.getSubcommand(); 177 | 178 | // if (sub == "score") { 179 | 180 | // } else if (sub == "total") { 181 | // const page = interaction.options.getNumber("page"); 182 | 183 | // const guildData = await guildDatas.find().sort({ totalScore: -1 }); 184 | // const guildScore = await guildDatas.findOne({ id: interaction.guild.id }); 185 | 186 | // const embed = new EmbedBuilder() 187 | // .setTitle("🏆 Total Score Leaderboard") 188 | // .setColor("#2f3136") 189 | // .setTimestamp(); 190 | 191 | // if (guildScore && guildScore.channelId !== "0") { 192 | // const rank = guildData.findIndex((b) => b.id === interaction.guild.id); 193 | 194 | // embed.setDescription( 195 | // `> ${interaction.guild.name} on at **#${ 196 | // rank + 1 197 | // }** in the leaderboard with **${guildScore.totalScore}** score!` 198 | // ); 199 | // } 200 | 201 | // if (guildScore && guildScore.channelId == "0") { 202 | // const rank = guildData.findIndex((b) => b.id === interaction.guild.id); 203 | 204 | // embed.setDescription( 205 | // `> ${interaction.guild.name} on at **#${ 206 | // rank + 1 207 | // }** in the leaderboard with **${ 208 | // guildScore.totalScore 209 | // }** score but counting channel is not setuped in this guild.` 210 | // ); 211 | // } 212 | 213 | // if (!guildScore) { 214 | // embed.setDescription( 215 | // `The counting channel is not setuped in this guild. Use **/setchannel** to get a position on leaderboard.` 216 | // ); 217 | // } 218 | 219 | // if (page) { 220 | // const pageNum = 10 * page - 10; 221 | // if (guildData.length < pageNum) { 222 | // return await interaction.reply({ 223 | // content: `❌ Unable to find page no **${page}**.`, 224 | // }); 225 | // } 226 | // if (guildData.length >= 11) { 227 | // embed.setFooter({ 228 | // text: `page ${page} of ${Math.ceil(guildData.length / 10)}`, 229 | // }); 230 | // } 231 | 232 | // for (const guild of guildData.splice(pageNum, 10)) { 233 | // const index = guildData.findIndex((b) => b.id == guild.id); 234 | // embed.addFields({ 235 | // name: `${ 236 | // index + 1 === 1 237 | // ? "🥇" 238 | // : `${ 239 | // index + 1 === 2 240 | // ? "🥈" 241 | // : `${index + 1 === 3 ? "🥉" : `#${index + 1}`}` 242 | // }` 243 | // } ${guild.name}`, 244 | // value: `> Score: ${guild.totalScore}`, 245 | // }); 246 | // } 247 | 248 | // return await interaction.reply({ embeds: [embed] }); 249 | // } 250 | 251 | // if (guildData.length >= 11) { 252 | // embed.setFooter({ 253 | // text: `page 1 of ${Math.ceil(guildData.length / 10)}`, 254 | // }); 255 | // } 256 | 257 | // for (const guild of guildData.slice(0, 10)) { 258 | // const index = guildData.findIndex((b) => b.id == guild.id); 259 | // embed.addFields({ 260 | // name: `${ 261 | // index + 1 === 1 262 | // ? "🥇" 263 | // : `${ 264 | // index + 1 === 2 265 | // ? "🥈" 266 | // : `${index + 1 === 3 ? "🥉" : `#${index + 1}`}` 267 | // }` 268 | // } ${guild.name}`, 269 | // value: `> Score: ${guild.totalScore}`, 270 | // }); 271 | // } 272 | // return await interaction.reply({ embeds: [embed] }); 273 | // } else if (sub == "user") { 274 | // const page = interaction.options.getNumber("page"); 275 | 276 | // const userData = await userDatas.find().sort({ score: -1 }); 277 | // const userScore = await userDatas.findOne({ id: interaction.user.id }); 278 | 279 | // const embed = new EmbedBuilder() 280 | // .setTitle("🏆 User Leaderboard") 281 | // .setColor("#2f3136") 282 | // .setTimestamp(); 283 | 284 | // if (userScore) { 285 | // const rank = userData.findIndex((b) => b.id === interaction.user.id); 286 | 287 | // embed.setDescription( 288 | // `> ***#${rank + 1}*** **${interaction.user.username}**` 289 | // ); 290 | // } 291 | 292 | // if (page) { 293 | // const pageNum = 10 * page - 10; 294 | // if (userData.length < pageNum) { 295 | // return await interaction.reply({ 296 | // content: `❌ Unable to find page no **${page}**.`, 297 | // }); 298 | // } 299 | // if (userData.length >= 11) { 300 | // embed.setFooter({ 301 | // text: `page ${page} of ${Math.ceil(userData.length / 10)}`, 302 | // }); 303 | // } 304 | 305 | // for (const user of userData.splice(pageNum, 10)) { 306 | // const index = userData.findIndex((b) => b.id == user.id); 307 | // embed.addFields({ 308 | // name: `${ 309 | // index + 1 === 1 310 | // ? "🥇" 311 | // : `${ 312 | // index + 1 === 2 313 | // ? "🥈" 314 | // : `${index + 1 === 3 ? "🥉" : `#${index + 1}`}` 315 | // }` 316 | // } ${user.name}`, 317 | // value: `> Score: ${user.score}`, 318 | // }); 319 | // } 320 | 321 | // return await interaction.reply({ embeds: [embed] }); 322 | // } 323 | 324 | // if (userData.length >= 11) { 325 | // embed.setFooter({ 326 | // text: `page 1 of ${Math.ceil(userData.length / 10)}`, 327 | // }); 328 | // } 329 | 330 | // for (const user of userData.slice(0, 10)) { 331 | // const index = userData.findIndex((b) => b.id == user.id); 332 | // embed.addFields({ 333 | // name: `${ 334 | // index + 1 === 1 335 | // ? "🥇" 336 | // : `${ 337 | // index + 1 === 2 338 | // ? "🥈" 339 | // : `${index + 1 === 3 ? "🥉" : `#${index + 1}`}` 340 | // }` 341 | // } ${user.name}`, 342 | // value: `> Score: ${user.score}`, 343 | // }); 344 | // } 345 | // return await interaction.reply({ embeds: [embed] }); 346 | // } else if (sub == "server") { 347 | // const page = interaction.options.getNumber("page"); 348 | 349 | // const guildData = await guildDatas.findOne({ id: interaction.guild.id }); 350 | 351 | // if (!guildData) { 352 | // return await interaction.reply( 353 | // "❌ The counting channel is not setuped in this guild." 354 | // ); 355 | // } 356 | 357 | // const userGuildData = await userGuildDatas 358 | // .find({ server: interaction.guild.id }) 359 | // .sort({ score: -1 }); 360 | // const userGuildScore = await guildDatas.findOne({ 361 | // id: interaction.user.id, 362 | // server: interaction.guild.id, 363 | // }); 364 | 365 | // if (!userGuildData) { 366 | // return await interaction.reply(`No user available in this server..`); 367 | // } 368 | 369 | // const embed = new EmbedBuilder() 370 | // .setTitle("🏆 Server Leaderboard") 371 | // .setColor("#2f3136") 372 | // .setTimestamp(); 373 | 374 | // if (userGuildScore) { 375 | // const rank = userGuildData.findIndex( 376 | // (b) => b.id === interaction.user.id 377 | // ); 378 | 379 | // embed.setDescription( 380 | // `> ***#${rank + 1}*** **${interaction.user.username}**` 381 | // ); 382 | // } 383 | 384 | // if (page) { 385 | // const pageNum = 10 * page - 10; 386 | // if (userGuildData.length < pageNum) { 387 | // return await interaction.reply({ 388 | // content: `❌ Unable to find page no **${page}**.`, 389 | // }); 390 | // } 391 | // if (userGuildData.length >= 11) { 392 | // embed.setFooter({ 393 | // text: `page ${page} of ${Math.ceil(userGuildData.length / 10)}`, 394 | // }); 395 | // } 396 | 397 | // for (const user of userGuildData.splice(pageNum, 10)) { 398 | // const index = userGuildData.findIndex((b) => b.id == user.id); 399 | // embed.addFields({ 400 | // name: `${ 401 | // index + 1 === 1 402 | // ? "🥇" 403 | // : `${ 404 | // index + 1 === 2 405 | // ? "🥈" 406 | // : `${index + 1 === 3 ? "🥉" : `#${index + 1}`}` 407 | // }` 408 | // } ${user.name}`, 409 | // value: `> Score: ${user.score}`, 410 | // }); 411 | // } 412 | 413 | // return await interaction.reply({ embeds: [embed] }); 414 | // } 415 | 416 | // if (userGuildData.length >= 11) { 417 | // embed.setFooter({ 418 | // text: `page 1 of ${Math.ceil(userGuildData.length / 10)}`, 419 | // }); 420 | // } 421 | 422 | // for (const user of userGuildData.slice(0, 10)) { 423 | // const index = userGuildData.findIndex((b) => b.id == user.id); 424 | // embed.addFields({ 425 | // name: `${ 426 | // index + 1 === 1 427 | // ? "🥇" 428 | // : `${ 429 | // index + 1 === 2 430 | // ? "🥈" 431 | // : `${index + 1 === 3 ? "🥉" : `#${index + 1}`}` 432 | // }` 433 | // } ${user.name}`, 434 | // value: `> Score: ${user.score}`, 435 | // }); 436 | // } 437 | // return await interaction.reply({ embeds: [embed] }); 438 | // } else { 439 | // return await interaction.reply( 440 | // `❌ Something went rong. **Try again letter**.` 441 | // ); 442 | // } 443 | // } 444 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/User/Rules.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { SlashCommandBuilder, EmbedBuilder, Colors } = require("discord.js"); 3 | const { t } = require("i18next"); 4 | 5 | class Rules extends Command { 6 | constructor(client, dir) { 7 | super(client, dir, { 8 | data: new SlashCommandBuilder() 9 | .setName("rules") 10 | .setDescription("🎗️ To check counting rules!") 11 | .setDMPermission(false), 12 | options: { 13 | category: "User", 14 | }, 15 | }); 16 | } 17 | /** 18 | * 19 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 20 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 21 | * @param {string} lng 22 | */ 23 | async execute(interaction, client, lng) { 24 | const embed = new EmbedBuilder() 25 | .setTitle("**Counting Rules!**") 26 | .setColor(Colors.DarkGreen) 27 | .setDescription( 28 | "> 1 • No Skipping Numbers\n> 2 • No Going Back In Numbers\n> 3 • Must alternate counters (except for solo mode)\n> 4 • No Botting, Scripting Or Abusing Bugs\n> 5 • Do Not Intentionally Ruin The Count" 29 | ) 30 | .setTimestamp() 31 | .setFooter({ text: "Bettel Counter - 2024" }); 32 | 33 | interaction.reply({ embeds: [embed] }); 34 | } 35 | } 36 | 37 | module.exports = Rules; 38 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/User/Stats.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { SlashCommandBuilder, EmbedBuilder, Colors } = require("discord.js"); 3 | const { t } = require("i18next"); 4 | 5 | class Stats extends Command { 6 | constructor(client, dir) { 7 | super(client, dir, { 8 | data: new SlashCommandBuilder() 9 | .setName("stats") 10 | .setDescription("To check server, user, bot counting stats!") 11 | .setDMPermission(false) 12 | .addSubcommand((subCommand) => 13 | subCommand 14 | .setName("server") 15 | .setDescription("To check server counting stats!") 16 | ) 17 | .addSubcommand((subCommand) => 18 | subCommand 19 | .setName("user") 20 | .setDescription("To check user counting stats!") 21 | .addUserOption((option) => 22 | option 23 | .setName("user") 24 | .setDescription("Select a user to view user stats.") 25 | .setRequired(false) 26 | ) 27 | ), 28 | options: { 29 | category: "User", 30 | }, 31 | }); 32 | } 33 | /** 34 | * 35 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 36 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 37 | * @param {string} lng 38 | */ 39 | async execute(interaction, client, lng) { 40 | const subCmd = interaction.options.getSubcommand(); 41 | let embed = new EmbedBuilder() 42 | .setColor(Colors.DarkGreen) 43 | .setTimestamp() 44 | .setFooter({ text: "Bettel Counter - 2024" }); 45 | 46 | switch (subCmd) { 47 | case "server": 48 | const gameData = await client.db.gameDatas.findOne({ 49 | guildId: interaction.guild.id, 50 | }); 51 | 52 | if (!gameData) { 53 | return interaction.reply({ 54 | content: "No data found for this server.", 55 | ephemeral: true, 56 | }); 57 | } 58 | 59 | const setupData = await client.db.setupDatas.findOne({ 60 | guildId: interaction.guild.id, 61 | }); 62 | let setupStats = "> Counting Channel: **Not setup yet!**"; 63 | if (setupData) { 64 | setupStats = `> Counting Channel: <#${setupData.setupChannel}> \n> Math Calculate: **${setupData.math}** \n> Number Only: **${setupData.numOnly}**`; 65 | } 66 | const totalCount = gameData.totalCount.right + gameData.totalCount.rong; 67 | const cranks = await client.db.gameDatas.find().sort({ count: -1 }); 68 | const crank = cranks.findIndex( 69 | (b) => b.guildId === interaction.guild.id 70 | ); 71 | const hranks = await client.db.gameDatas 72 | .find() 73 | .sort({ highestCount: -1 }); 74 | const hrank = hranks.findIndex( 75 | (b) => b.guildId === interaction.guild.id 76 | ); 77 | embed.setTitle("Server Counting Stats"); 78 | embed.setDescription( 79 | `> Current Count: **${gameData.count}** (**#${ 80 | crank + 1 81 | }**) \n> Highest Count: **${gameData.highestCount}** (**#${ 82 | hrank + 1 83 | }**) \n> Total Count: **${totalCount}** \n> Correct Rate: **${( 84 | (gameData.totalCount.right / totalCount) * 85 | 100 86 | ).toFixed(2)}%** \n> Last Counter: <@${ 87 | gameData.lastCounter 88 | }> \n> Server Saves: **${gameData.saves}/${ 89 | gameData.saveSlot 90 | }** \n${setupStats}` 91 | ); 92 | await interaction.reply({ 93 | embeds: [embed], 94 | }); 95 | break; 96 | case "user": 97 | const user = 98 | (await interaction.options.getMember("user")?.user) || 99 | interaction.user; 100 | const userData = await client.db.userDatas.findOne({ 101 | userId: user.id, 102 | }); 103 | 104 | if (!userData) { 105 | return interaction.reply({ 106 | content: "No data found for this user.", 107 | ephemeral: true, 108 | }); 109 | } 110 | const totalUserCount = userData.count.right + userData.count.rong; 111 | embed.setTitle(user.username).addFields({ 112 | name: "Global Stats", 113 | value: `> Total Count: **${totalUserCount}** \n> Correct Rate: **${( 114 | (userData.count.right / totalUserCount) * 115 | 100 116 | ).toFixed(2)}%** \n> Correct(\`✅\`): **${ 117 | userData.count.right 118 | }** \n> Wrong(\`❌\`): **${userData.count.rong}** \n> Score: **${ 119 | userData.score 120 | }** \n> User Saves: **${userData.saves}/${userData.saveSlot}** `, 121 | inline: true, 122 | }); 123 | await interaction.reply({ 124 | embeds: [embed], 125 | }); 126 | break; 127 | default: 128 | break; 129 | } 130 | } 131 | } 132 | 133 | module.exports = Stats; 134 | -------------------------------------------------------------------------------- /Interactions/SlashCommands/User/Vote.js: -------------------------------------------------------------------------------- 1 | const Command = require("../../../Structures/Classes/BaseCommand"); 2 | const { 3 | SlashCommandBuilder, 4 | EmbedBuilder, 5 | Colors, 6 | ActionRowBuilder, 7 | ButtonBuilder, 8 | ButtonStyle, 9 | } = require("discord.js"); 10 | const { t } = require("i18next"); 11 | 12 | class Vote extends Command { 13 | constructor(client, dir) { 14 | super(client, dir, { 15 | data: new SlashCommandBuilder() 16 | .setName("vote") 17 | .setDescription("To vote Bettel Counter!") 18 | .setDMPermission(false), 19 | options: { 20 | category: "User", 21 | }, 22 | }); 23 | } 24 | /** 25 | * 26 | * @param {import("discord.js").ChatInputCommandInteraction} interaction 27 | * @param {import("../../../Structures/Classes/BotClient").BotClient} client 28 | * @param {string} lng 29 | */ 30 | async execute(interaction, client, lng) { 31 | const userData = await client.db.userDatas.findOne({ 32 | userId: interaction.user.id, 33 | }); 34 | if (!userData) { 35 | client.db.userDatas.create({ 36 | userId: vote.user, 37 | }); 38 | userData = await client.db.userDatas.findOne({ 39 | userId: vote.user, 40 | }); 41 | } 42 | const voteMs = 1000 * 60 * 60 * 12; 43 | const resolve = new Date() - userData.vote.time; 44 | const embed = new EmbedBuilder().setColor(Colors.DarkGreen); 45 | const row = new ActionRowBuilder().addComponents( 46 | new ButtonBuilder() 47 | .setLabel("Vote Bettel Counter") 48 | .setURL(client.config.voteUrl) 49 | .setStyle(ButtonStyle.Link) 50 | ); 51 | 52 | if (resolve < voteMs) { 53 | const remainingTimeHours = Math.floor( 54 | (voteMs - resolve) / (1000 * 60 * 60) 55 | ); 56 | const remainingTimeMinutes = Math.floor( 57 | ((voteMs - resolve) % (1000 * 60 * 60)) / (1000 * 60) 58 | ); 59 | 60 | embed.setDescription( 61 | `**You have alrady voted.** \n\n> You can vote after **${remainingTimeHours}** hour, **${remainingTimeMinutes}** minute.` 62 | ); 63 | await interaction.reply({ embeds: [embed], components: [row] }); 64 | } else { 65 | embed.setDescription( 66 | `**Your vote is available.** \n\n> Vote now by click **[here](https://top.gg/bot/1106236979147964426/vote)**!` 67 | ); 68 | await interaction.reply({ embeds: [embed], components: [row] }); 69 | } 70 | } 71 | } 72 | 73 | module.exports = Vote; 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Counting Bot 2 | 3 | A powerful and customizable counting bot for Discord servers. Keep track of counting games, enforce rules, and log activities efficiently. 4 | 5 | ## Features 6 | 7 | - Enforces counting rules 8 | - Tracks counting progress 9 | - Logs counting activities to a dedicated channel 10 | - Supports multiple servers 11 | - Redis and MongoDB integration for efficient data management 12 | - Admin controls and developer features 13 | 14 | ## Setup & Installation 15 | 16 | ### Prerequisites 17 | 18 | - [Node.js](https://nodejs.org/) v16 or later 19 | - [MongoDB](https://www.mongodb.com/) database 20 | - [Redis](https://redis.io/) server 21 | - A Discord bot token from the [Discord Developer Portal](https://discord.com/developers/applications) 22 | 23 | ### Installation Steps 24 | 25 | 1. Clone the repository: 26 | ```sh 27 | git clone https://github.com/jasonmidul/CountingBot.git 28 | cd CountingBot 29 | ``` 30 | 2. Install dependencies: 31 | ```sh 32 | npm install 33 | ``` 34 | 3. Create a `.env` file in the project root and add your credentials: 35 | ```env 36 | token="your bot token" 37 | mongoUrl="mongodb+srv://xyz" 38 | logWebhook="https://discord.com/api/webhooks/xyz" 39 | redis="redis://xyz" 40 | ``` 41 | 4. Run the bot: 42 | ```sh 43 | node index.js 44 | ``` 45 | 46 | ## Configuration 47 | 48 | Modify `config.js` to set up bot settings: 49 | 50 | - `botToken`: Your Discord bot token 51 | - `mongoUrl`: MongoDB connection string 52 | - `redis`: Redis database URL 53 | - `clientId`: Bot's client ID 54 | - `logChannel`: Log channel ID 55 | - `voteLog`: Voting log channel ID 56 | - `deploySlashOnReady`: Whether to deploy slash commands on startup 57 | - `underDevelopment`: Toggle development mode 58 | - `developers`: List of bot developers 59 | - `devGuilds`: List of development servers 60 | - `betaTestGuilds`: List of beta test servers 61 | - `cTopic`: Counting rules message 62 | - `voteUrl`: URL to vote for the bot 63 | - `logWebhook`: Webhook URL for logging 64 | 65 | ## Counting Rules 66 | 67 | 1. No Skipping Numbers 68 | 2. No Going Back In Numbers 69 | 3. Must Alternate Counters (except for solo mode) 70 | 4. No Botting, Scripting, or Abusing Bugs 71 | 5. Do Not Intentionally Ruin The Count 72 | 73 | ## Support 74 | 75 | For issues or suggestions, open an issue on the [GitHub repository](https://github.com/jasonmidul/CountingBot). 76 | 77 | ## License 78 | 79 | This project is licensed under the MIT License. See `LICENSE` for details. 80 | 81 | -------------------------------------------------------------------------------- /Schemas/Bot/BotDatas.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const botSchema = new Schema({ 4 | password: { 5 | type: String, 6 | required: true, 7 | }, 8 | cmdUsed: { 9 | type: Number, 10 | default: 0, 11 | }, 12 | count: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | }); 17 | 18 | module.exports = model("BotDatas", botSchema); 19 | -------------------------------------------------------------------------------- /Schemas/Counting/GameData.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const gameData = new Schema({ 4 | guildId: { 5 | type: String, 6 | required: true, 7 | }, 8 | name: { 9 | type: String, 10 | default: "undefined" 11 | }, 12 | lastCounter: { 13 | type: String, 14 | required: false, 15 | default: "none", 16 | }, 17 | count: { 18 | type: Number, 19 | default: 0, 20 | }, 21 | totalCount: { 22 | right: { 23 | type: Number, 24 | default: 0, 25 | }, 26 | rong: { 27 | type: Number, 28 | default: 0, 29 | }, 30 | }, 31 | highestCount: { 32 | type: Number, 33 | default: 0, 34 | }, 35 | saves: { 36 | type: Number, 37 | default: 2, 38 | }, 39 | saveSlot: { 40 | type: Number, 41 | default: 2, 42 | }, 43 | reaction: { 44 | success: { 45 | type: String, 46 | default: "✅", 47 | }, 48 | fail: { 49 | type: String, 50 | default: "❌", 51 | }, 52 | save: { 53 | type: String, 54 | default: "⛑️", 55 | }, 56 | warn: { 57 | type: String, 58 | default: "⚠️", 59 | }, 60 | }, 61 | }); 62 | 63 | module.exports = model("GameData", gameData); 64 | -------------------------------------------------------------------------------- /Schemas/Counting/SetupData.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const setupData = new Schema({ 4 | guildId: { 5 | type: String, 6 | required: true, 7 | }, 8 | setupChannel: { 9 | type: String, 10 | required: true, 11 | }, 12 | math: { 13 | type: Boolean, 14 | required: false, 15 | default: true, 16 | }, 17 | numOnly: { 18 | type: Boolean, 19 | required: false, 20 | default: false, 21 | }, 22 | }); 23 | 24 | module.exports = model("SetupData", setupData); 25 | -------------------------------------------------------------------------------- /Schemas/Server/LanguageData.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const languageData = new Schema({ 4 | guildId: { 5 | type: String, 6 | required: true, 7 | }, 8 | lng: { 9 | type: String, 10 | default: "en", 11 | }, 12 | }); 13 | 14 | module.exports = model("LanguageData", languageData); 15 | -------------------------------------------------------------------------------- /Schemas/User/UserData.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const userData = new Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | name: { 9 | type: String, 10 | default: "undefined" 11 | }, 12 | score: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | count: { 17 | right: { 18 | type: Number, 19 | default: 0, 20 | }, 21 | rong: { 22 | type: Number, 23 | default: 0, 24 | }, 25 | }, 26 | saves: { 27 | type: Number, 28 | default: 2, 29 | }, 30 | saveSlot: { 31 | type: Number, 32 | default: 4, 33 | }, 34 | vote: { 35 | count: { 36 | type: Number, 37 | default: 0, 38 | }, 39 | time: { 40 | type: Date, 41 | default: new Date(0), 42 | }, 43 | }, 44 | }); 45 | 46 | module.exports = model("UserData", userData); 47 | -------------------------------------------------------------------------------- /Schemas/index.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const botDatas = require("./Bot/BotDatas"); 3 | const setupDatas = require("./Counting/SetupData"); 4 | const gameDatas = require("./Counting/GameData"); 5 | const languageDatas = require("./Server/LanguageData"); 6 | const userDatas = require("./User/UserData"); 7 | const { Logger } = require("../Structures/Functions/index"); 8 | const logger = new Logger(); 9 | 10 | function ConnectMongo(client) { 11 | if (client.config.mongoUrl) { 12 | logger.info("Trying to connect with database..."); 13 | mongoose.set("strictQuery", false); 14 | mongoose 15 | .connect(client.config.mongoUrl) 16 | .then((data) => { 17 | logger.success( 18 | `Database has been connected to: "${data.connection.name}"` 19 | ); 20 | }) 21 | .catch((err) => logger.error(err)); 22 | } else logger.warn(`You forget to add mongoUrl in config.js`); 23 | } 24 | 25 | module.exports = { 26 | ConnectMongo, 27 | botDatas, 28 | setupDatas, 29 | gameDatas, 30 | userDatas, 31 | languageDatas, 32 | }; 33 | -------------------------------------------------------------------------------- /Structures/Classes/BaseCommand.js: -------------------------------------------------------------------------------- 1 | module.exports = class Command { 2 | constructor(client, dir, option) { 3 | this.client = client; 4 | this.data = option.data; 5 | this.name = option.data.name; 6 | this.options = option.options; 7 | this.category = dir; 8 | } 9 | async execute(...args) { 10 | return await Promise.resolve(); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /Structures/Classes/BaseComponent.js: -------------------------------------------------------------------------------- 1 | module.exports = class Component { 2 | constructor(client, option) { 3 | this.client = client; 4 | this.id = option.id || option.name; 5 | this.options = option.options; 6 | } 7 | async execute(...args) { 8 | return await Promise.resolve(); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /Structures/Classes/BaseEvent.js: -------------------------------------------------------------------------------- 1 | module.exports = class Event { 2 | constructor(client, options) { 3 | this.client = client; 4 | this.name = options.name; 5 | this.ONCE = options.once || false; 6 | } 7 | async execute(...args) { 8 | return await Promise.resolve(); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /Structures/Classes/BotClient.js: -------------------------------------------------------------------------------- 1 | const { Client, Collection } = require("discord.js"); 2 | const Redis = require("ioredis"); 3 | const { EventHandler } = require("../Handlers/EventHandler"); 4 | const { Logger } = require("../Functions/index"); 5 | const Config = require("../../config"); 6 | const db = require("../../Schemas/index"); 7 | const logger = new Logger(); 8 | 9 | class BotClient extends Client { 10 | constructor(options) { 11 | super(options); 12 | 13 | this.config = Config; 14 | this.events = new Collection(); 15 | this.buttons = new Collection(); 16 | this.modals = new Collection(); 17 | this.autoComplete = new Collection(); 18 | this.slashCommands = new Collection(); 19 | this.db = db; 20 | this.redis = new Redis(this.config.redis); 21 | } 22 | async start() { 23 | await this.registerModules(); 24 | await this.login(this.config.botToken); 25 | } 26 | async registerModules() { 27 | const { loadEvents } = new EventHandler(); 28 | 29 | try { 30 | await loadEvents(this); 31 | } catch (error) { 32 | logger.error(error); 33 | } 34 | } 35 | } 36 | 37 | module.exports = { BotClient }; 38 | -------------------------------------------------------------------------------- /Structures/Functions/JsonFind.js: -------------------------------------------------------------------------------- 1 | module.exports = function guildFind(id, ids) { 2 | let match; 3 | ids.forEach(async (guild) => { 4 | if (id == guild.id) { 5 | match = true; 6 | } 7 | }); 8 | return match; 9 | }; 10 | -------------------------------------------------------------------------------- /Structures/Functions/Logger.js: -------------------------------------------------------------------------------- 1 | const { EmbedBuilder, WebhookClient, Colors } = require("discord.js"); 2 | const { inspect } = require("util"); 3 | const config = require("../../config"); 4 | const webhook = new WebhookClient({ 5 | url: config.logWebhook, 6 | }); 7 | 8 | class Logger { 9 | constructor() { 10 | this.origin = this._getLogOrigin().split(/[\\/]/).pop(); 11 | } 12 | _getLogOrigin() { 13 | let filename; 14 | 15 | let _pst = Error.prepareStackTrace; 16 | Error.prepareStackTrace = function (err, stack) { 17 | return stack; 18 | }; 19 | try { 20 | let err = new Error(); 21 | let callerfile; 22 | let currentfile; 23 | 24 | currentfile = err.stack.shift().getFileName(); 25 | 26 | while (err.stack.length) { 27 | callerfile = err.stack.shift().getFileName(); 28 | 29 | if (currentfile !== callerfile) { 30 | filename = callerfile; 31 | break; 32 | } 33 | } 34 | } catch (err) {} 35 | Error.prepareStackTrace = _pst; 36 | 37 | return filename; 38 | } 39 | 40 | error(content) { 41 | const output = 42 | new Date().toLocaleTimeString() + 43 | ` 🛑 [` + 44 | `${ 45 | this.origin.length > 25 46 | ? this.origin.substring(0, 17) + "..." 47 | : this.origin 48 | }` + 49 | `] ` + 50 | " ".repeat(20 - (this.origin.length > 20 ? 20 : this.origin.length)) + 51 | "| " + 52 | content; 53 | webhook.send({ 54 | content: `> \`\`\`${output}\`\`\``, 55 | }); 56 | console.log(output); 57 | } 58 | 59 | info(content) { 60 | const output = 61 | new Date().toLocaleTimeString() + 62 | ` ✉️ [` + 63 | `${ 64 | this.origin.length > 25 65 | ? this.origin.substring(0, 17) + "..." 66 | : this.origin 67 | }` + 68 | `] ` + 69 | " ".repeat(20 - (this.origin.length > 20 ? 20 : this.origin.length)) + 70 | "| " + 71 | content; 72 | webhook.send({ 73 | content: `> \`\`\`${output}\`\`\``, 74 | }); 75 | console.log(output); 76 | } 77 | warn(content) { 78 | const output = 79 | new Date().toLocaleTimeString() + 80 | ` ⚠️ [` + 81 | `${ 82 | this.origin.length > 25 83 | ? this.origin.substring(0, 17) + "..." 84 | : this.origin 85 | }` + 86 | `] ` + 87 | " ".repeat(20 - (this.origin.length > 20 ? 20 : this.origin.length)) + 88 | "| " + 89 | content; 90 | webhook.send({ 91 | content: `> \`\`\`${output}\`\`\``, 92 | }); 93 | console.log(output); 94 | } 95 | 96 | success(content) { 97 | const output = 98 | new Date().toLocaleTimeString() + 99 | ` ✅ [` + 100 | `${ 101 | this.origin.length > 25 102 | ? this.origin.substring(0, 17) + "..." 103 | : this.origin 104 | }` + 105 | `] ` + 106 | " ".repeat(20 - (this.origin.length > 20 ? 20 : this.origin.length)) + 107 | "| " + 108 | content; 109 | webhook.send({ 110 | content: `> \`\`\`${output}\`\`\``, 111 | }); 112 | console.log(output); 113 | } 114 | custom(content) { 115 | console.log( 116 | new Date().toLocaleTimeString() + 117 | ` 🛑 [` + 118 | `${ 119 | this.origin.length > 20 120 | ? this.origin.substring(0, 17) + "..." 121 | : this.origin 122 | }` + 123 | `] ` + 124 | " ".repeat(20 - (this.origin.length > 20 ? 20 : this.origin.length)) + 125 | "| " + 126 | content 127 | ); 128 | } 129 | } 130 | 131 | module.exports = { Logger }; 132 | -------------------------------------------------------------------------------- /Structures/Functions/PaginationEmbed.js: -------------------------------------------------------------------------------- 1 | const { 2 | ActionRowBuilder, 3 | ButtonBuilder, 4 | ButtonStyle, 5 | EmbedBuilder, 6 | } = require("discord.js"); 7 | 8 | module.exports = async (interaction, embeds) => { 9 | const pages = {}; 10 | const getRow = (id) => { 11 | return new ActionRowBuilder().addComponents( 12 | new ButtonBuilder() 13 | .setLabel("◀") 14 | .setCustomId("prev_embed") 15 | .setStyle(ButtonStyle.Primary) 16 | .setDisabled(pages[id] === 0), 17 | new ButtonBuilder() 18 | .setLabel("🗑️") 19 | .setCustomId("delete") 20 | .setStyle(ButtonStyle.Danger), 21 | new ButtonBuilder() 22 | .setLabel("▶") 23 | .setCustomId("next_embed") 24 | .setStyle(ButtonStyle.Primary) 25 | .setDisabled(pages[id] === embeds.length - 1) 26 | ); 27 | }; 28 | 29 | const id = interaction.user.id; 30 | pages[id] = pages[id] || 0; 31 | let Pagemax = embeds.length; 32 | 33 | const embed = embeds[pages[id]]; 34 | 35 | await embeds[pages[id]].setFooter({ 36 | text: `Page ${pages[id] + 1} from ${Pagemax}`, 37 | }); 38 | 39 | await interaction.reply({ 40 | embeds: [embed], 41 | components: [getRow(id)], 42 | }); 43 | 44 | let replyEmbed = await interaction.fetchReply(); 45 | 46 | const filter = (i) => i.user.id === interaction.user.id; 47 | const time = 1000 * 60 * 5; 48 | 49 | const collector = await replyEmbed.createMessageComponentCollector({ 50 | filter, 51 | time, 52 | }); 53 | 54 | collector.on("collect", async (b) => { 55 | if (!b) return; 56 | if (b.customId !== "prev_embed" && b.customId !== "next_embed") return; 57 | 58 | b.deferUpdate(); 59 | 60 | if (b.customId === "prev_embed" && pages[id] > 0) { 61 | --pages[id]; 62 | } else if (b.customId === "next_embed" && pages[id] < embeds.length - 1) { 63 | ++pages[id]; 64 | } 65 | 66 | await embeds[pages[id]].setFooter({ 67 | text: `Page ${pages[id] + 1} of ${Pagemax}`, 68 | }); 69 | 70 | await interaction.editReply({ 71 | embeds: [embeds[pages[id]]], 72 | components: [getRow(id)], 73 | }); 74 | 75 | replyEmbed = await interaction.fetchReply(); 76 | }); 77 | collector.on("end", async () => { 78 | try { 79 | await interaction.editReply({ 80 | components: [ 81 | new ActionRowBuilder().addComponents( 82 | new ButtonBuilder() 83 | .setLabel("◀") 84 | .setCustomId("prev_embed") 85 | .setStyle(ButtonStyle.Primary) 86 | .setDisabled(true), 87 | new ButtonBuilder() 88 | .setLabel("🗑️") 89 | .setCustomId("delete") 90 | .setStyle(ButtonStyle.Danger), 91 | new ButtonBuilder() 92 | .setLabel("▶") 93 | .setCustomId("next_embed") 94 | .setStyle(ButtonStyle.Primary) 95 | .setDisabled(true) 96 | ), 97 | ], 98 | }); 99 | } catch (error) {} 100 | }); 101 | }; 102 | -------------------------------------------------------------------------------- /Structures/Functions/VoteLog.js: -------------------------------------------------------------------------------- 1 | const { EmbedBuilder, Colors } = require("discord.js"); 2 | 3 | async function voteLog(client, id, name) { 4 | const channel = await client.channels.cache.get(client.config.voteLog); 5 | 6 | const image = `https://top.gg/api/widget/${client.config.clientId}.png`; 7 | 8 | await channel.send({ 9 | embeds: [ 10 | new EmbedBuilder() 11 | .setTitle(`${name} Just Voted!`) 12 | .setImage(image) 13 | .setColor(Colors.DarkGreen) 14 | .setTimestamp() 15 | .setDescription( 16 | `Thank's for voting **[Counting Bot](${client.config.voteUrl})**.` 17 | ) 18 | .setFooter({ 19 | text: `Counting Bot - 2024`, 20 | }), 21 | ], 22 | }); 23 | } 24 | 25 | module.exports = { voteLog }; 26 | -------------------------------------------------------------------------------- /Structures/Functions/index.js: -------------------------------------------------------------------------------- 1 | const jsonFind = require("./JsonFind"); 2 | const { Logger } = require("./Logger"); 3 | const { voteLog } = require("./VoteLog"); 4 | const PaginationEmbed = require("./PaginationEmbed"); 5 | 6 | module.exports = { jsonFind, Logger, voteLog, PaginationEmbed }; 7 | -------------------------------------------------------------------------------- /Structures/Handlers/CommandHandler.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const AsciiTable = require("ascii-table"); 4 | const { REST } = require("@discordjs/rest"); 5 | const { Routes } = require("discord.js"); 6 | const { Logger } = require("../Functions/index"); 7 | const logger = new Logger(); 8 | 9 | class CommandHandler { 10 | constructor() {} 11 | 12 | async loadCommands(client, update) { 13 | const commandPath = fs.readdirSync( 14 | path.join(__dirname, "../../Interactions/SlashCommands") 15 | ); 16 | const CommandsTable = new AsciiTable() 17 | .setHeading( 18 | "⠀⠀⠀⠀", 19 | "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Slash Command⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", 20 | "⠀⠀Status⠀⠀" 21 | ) 22 | .setBorder("┋", "═", "●", "●") 23 | .setAlign(2, AsciiTable.CENTER); 24 | 25 | await client.slashCommands.clear(); 26 | let commandArray = []; 27 | let devCommandArray = []; 28 | let devCmdCount = 0; 29 | let cmdCount = 0; 30 | let i = 1; 31 | 32 | commandPath.forEach((dir) => { 33 | const commandFolder = fs 34 | .readdirSync( 35 | path.join(__dirname, `../../Interactions/SlashCommands/${dir}`) 36 | ) 37 | .filter((file) => file.endsWith(".js")); 38 | 39 | commandFolder.forEach(async (file) => { 40 | const commandFile = require(`../../Interactions/SlashCommands/${dir}/${file}`); 41 | const command = new commandFile(client, dir); 42 | 43 | if (dir == "Dev") { 44 | client.slashCommands.set(command.data.name, command); 45 | devCommandArray.push(command.data.toJSON()); 46 | devCmdCount++; 47 | CommandsTable.addRow( 48 | (i++).toString() + ".", 49 | command.name + "(dev)", 50 | "» 🌱 «" 51 | ); 52 | } else { 53 | client.slashCommands.set(command.data.name, command); 54 | commandArray.push(command.data.toJSON()); 55 | cmdCount++; 56 | CommandsTable.addRow((i++).toString() + ".", command.name, "» 🌱 «"); 57 | } 58 | }); 59 | }); 60 | console.log(CommandsTable.toString()); 61 | logger.info(` • ${cmdCount} Slash commands has been loaded.`); 62 | logger.info(` • ${devCmdCount} Developer commands has been loaded.`); 63 | 64 | if (update) { 65 | const rest = new REST({ version: "10" }).setToken(client.config.botToken); 66 | await (async () => { 67 | try { 68 | await rest.put(Routes.applicationCommands(client.config.clientId), { 69 | body: commandArray, 70 | }); 71 | logger.success( 72 | ` • ${cmdCount} Slash commands has been registered globally.` 73 | ); 74 | client.config.devGuilds.forEach(async (guild) => { 75 | await rest.put( 76 | Routes.applicationGuildCommands(client.config.clientId, guild.id), 77 | { 78 | body: devCommandArray, 79 | } 80 | ); 81 | logger.warn( 82 | ` • Dev Commands registered for guild "${guild.name}"` 83 | ); 84 | }); 85 | } catch (error) { 86 | logger.error(error); 87 | } 88 | })(); 89 | } 90 | } 91 | } 92 | 93 | module.exports = { CommandHandler }; 94 | -------------------------------------------------------------------------------- /Structures/Handlers/ComponentHandler.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const { Logger } = require("../Functions/index"); 4 | const logger = new Logger(); 5 | 6 | class ComponentHandler { 7 | constructor() {} 8 | 9 | async loadComponents(client) { 10 | const componentPath = fs.readdirSync( 11 | path.join(__dirname, "../../Interactions/Components") 12 | ); 13 | 14 | await client.buttons.clear(); 15 | await client.modals.clear(); 16 | await client.autoComplete.clear(); 17 | let buttonCount = 0; 18 | let modalCount = 0; 19 | let autoCompleteCount = 0; 20 | 21 | componentPath.forEach((dir) => { 22 | const componentFolder = fs 23 | .readdirSync( 24 | path.join(__dirname, `../../Interactions/Components/${dir}`) 25 | ) 26 | .filter((file) => file.endsWith(".js")); 27 | 28 | componentFolder.forEach(async (file) => { 29 | const componentFile = require(`../../Interactions/Components/${dir}/${file}`); 30 | const component = new componentFile(client); 31 | switch (dir) { 32 | case "Buttons": 33 | client.buttons.set(component.id, component); 34 | buttonCount++; 35 | break; 36 | case "Modals": 37 | client.modals.set(component.id, component); 38 | modalCount++; 39 | break; 40 | case "AutoComplete": 41 | client.autoComplete.set(component.id, component); 42 | autoCompleteCount++; 43 | break; 44 | } 45 | }); 46 | }); 47 | 48 | logger.info(`${buttonCount} Buttons has been loaded.`); 49 | logger.info(`${modalCount} Modals has been loaded.`); 50 | logger.info(`${autoCompleteCount} AutoComplete has been loaded.`); 51 | } 52 | } 53 | 54 | module.exports = { ComponentHandler }; 55 | -------------------------------------------------------------------------------- /Structures/Handlers/ErrorHandler.js: -------------------------------------------------------------------------------- 1 | const { WebhookClient } = require("discord.js"); 2 | const { inspect } = require("util"); 3 | const { Logger } = require("../Functions/index"); 4 | const logger = new Logger(); 5 | const config = require("../../config"); 6 | 7 | async function ClientErrorHandler(client) { 8 | const webhook = new WebhookClient({ 9 | url: config.logWebhook, 10 | }); 11 | client.on("error", (err) => { 12 | logger.custom(`${err}`); 13 | return webhook.send({ 14 | content: `⛔ **Discord API Error** \`\`\`${inspect(err, { 15 | depth: 0, 16 | }).slice(0, 1990)}\`\`\``, 17 | }); 18 | }); 19 | } 20 | async function ErrorHandler() { 21 | const webhook = new WebhookClient({ 22 | url: config.logWebhook, 23 | }); 24 | logger.success("Error Handler has been loaded"); 25 | 26 | process.on("unhandledRejection", (reason, promise) => { 27 | logger.custom(`${reason}`); 28 | 29 | webhook.send({ 30 | content: `## ‼️ Unhandled Rejection/Catch`, 31 | }); 32 | webhook.send({ 33 | content: `**Reason** \`\`\`${inspect(reason, { depth: 0 }).slice( 34 | 0, 35 | 1990 36 | )}\`\`\``, 37 | }); 38 | return webhook.send({ 39 | content: `**Promise** \`\`\`${inspect(promise, { depth: 0 }).slice( 40 | 0, 41 | 1990 42 | )}\`\`\``, 43 | }); 44 | }); 45 | 46 | process.on("uncaughtException", (err, origin) => { 47 | logger.custom(`${err}`); 48 | 49 | webhook.send({ 50 | content: `## ‼️ Uncaught Exception/Catch`, 51 | }); 52 | webhook.send({ 53 | content: `**Error** \`\`\`${inspect(err, { depth: 0 }).slice( 54 | 0, 55 | 1990 56 | )}\`\`\``, 57 | }); 58 | return webhook.send({ 59 | content: `**Origin** \`\`\`${inspect(origin, { depth: 0 }).slice( 60 | 0, 61 | 1990 62 | )}\`\`\``, 63 | }); 64 | }); 65 | 66 | process.on("uncaughtExceptionMonitor", (err, origin) => { 67 | logger.custom(`${err}`); 68 | 69 | webhook.send({ 70 | content: `## ‼️ Uncaught Exception Monitor`, 71 | }); 72 | webhook.send({ 73 | content: `**Error** \`\`\`${inspect(err, { depth: 0 }).slice( 74 | 0, 75 | 1990 76 | )}\`\`\``, 77 | }); 78 | return webhook.send({ 79 | content: `**Origin** \`\`\`${inspect(origin, { depth: 0 }).slice( 80 | 0, 81 | 1990 82 | )}\`\`\``, 83 | }); 84 | }); 85 | 86 | process.on("warning", (warn) => { 87 | logger.custom(`${warn}`); 88 | 89 | webhook.send({ 90 | content: `## ⚠️ Uncaught Exception Monitor Warning`, 91 | }); 92 | return webhook.send({ 93 | content: `**Warn** \`\`\`${inspect(warn, { depth: 0 }).slice( 94 | 0, 95 | 1990 96 | )}\`\`\``, 97 | }); 98 | }); 99 | } 100 | 101 | module.exports = { ErrorHandler, ClientErrorHandler }; 102 | -------------------------------------------------------------------------------- /Structures/Handlers/EventHandler.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const AsciiTable = require("ascii-table"); 4 | const { Logger } = require("../Functions/index"); 5 | const logger = new Logger(); 6 | 7 | class EventHandler { 8 | constructor() {} 9 | 10 | async loadEvents(client, gg) { 11 | const EventsTable = new AsciiTable() 12 | .setHeading( 13 | "⠀⠀⠀⠀", 14 | "⠀⠀⠀⠀⠀⠀⠀⠀Events⠀⠀⠀⠀⠀⠀⠀⠀", 15 | "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀File⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", 16 | "⠀⠀Status⠀⠀" 17 | ) 18 | .setBorder("┋", "═", "●", "●") 19 | .setAlign(3, AsciiTable.CENTER); 20 | const eventPath = fs.readdirSync(path.join(__dirname, "../../Events")); 21 | 22 | await client.events.clear(); 23 | let eventCount = 0; 24 | 25 | eventPath.forEach((dir) => { 26 | const eventFolder = fs 27 | .readdirSync(path.join(__dirname, `../../Events/${dir}`)) 28 | .filter((file) => file.endsWith(".js")); 29 | 30 | eventFolder.forEach(async (file) => { 31 | const eventFile = require(`../../Events/${dir}/${file}`); 32 | 33 | const event = new eventFile(client); 34 | eventCount++; 35 | EventsTable.addRow( 36 | eventCount.toString() + ".", 37 | event.name, 38 | file, 39 | "» 🌱 «" 40 | ); 41 | const execute = (...args) => event.execute(...args, client); 42 | client.events.set(file, { 43 | execute: execute, 44 | name: event.name, 45 | }); 46 | 47 | if (event.ONCE) { 48 | client.once(event.name, execute); 49 | } else { 50 | client.on(event.name, execute); 51 | } 52 | }); 53 | }); 54 | console.log(EventsTable.toString()); 55 | logger.success(` • ${eventCount} Events has been loaded.`); 56 | } 57 | } 58 | 59 | module.exports = { EventHandler }; 60 | -------------------------------------------------------------------------------- /Structures/Handlers/LanguageHandler.js: -------------------------------------------------------------------------------- 1 | const i18next = require("i18next"); 2 | const resources = require("../../locales/resources"); 3 | 4 | function loadLanguages() { 5 | i18next.init({ 6 | fallbackLng: "en", 7 | defaultNS: "system", 8 | interpolation: { 9 | escapeValue: false, 10 | }, 11 | resources, 12 | }); 13 | } 14 | module.exports = { loadLanguages }; 15 | -------------------------------------------------------------------------------- /bot.js: -------------------------------------------------------------------------------- 1 | const { GatewayIntentBits, Partials } = require("discord.js"); 2 | const { BotClient } = require("./Structures/Classes/BotClient"); 3 | const { 4 | ErrorHandler, 5 | ClientErrorHandler, 6 | } = require("./Structures/Handlers/ErrorHandler"); 7 | const { ClusterClient, getInfo } = require("discord-hybrid-sharding"); 8 | 9 | const clientOptions = { 10 | allowedMentions: { 11 | parse: ["users", "roles", "everyone"], 12 | repliedUser: false, 13 | }, 14 | intents: [ 15 | GatewayIntentBits.Guilds, 16 | GatewayIntentBits.MessageContent, 17 | GatewayIntentBits.GuildMembers, 18 | GatewayIntentBits.GuildMessages, 19 | ], 20 | partials: [ 21 | Partials.Channel, 22 | Partials.Message, 23 | Partials.User, 24 | Partials.GuildMember, 25 | ], 26 | shards: ClusterClient.getInfo().SHARD_LIST, 27 | shardCount: ClusterClient.getInfo().TOTAL_SHARDS, 28 | }; 29 | 30 | const client = new BotClient(clientOptions); 31 | client.cluster = new ClusterClient(client); 32 | 33 | ErrorHandler(); 34 | ClientErrorHandler(client); 35 | 36 | client.start(); 37 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const dotenv = require("dotenv"); 2 | dotenv.config(); 3 | 4 | module.exports = { 5 | botToken: process.env.token, 6 | mongoUrl: process.env.mongoUrl, 7 | redis: process.env.redis, 8 | clientId: "1293072781491044415", 9 | logChannel: "1293079076697018469", 10 | voteLog: "1293079113787113493", 11 | deploySlashOnReady: true, 12 | underDevelopment: false, 13 | developers: [ 14 | { 15 | name: "Jason Midul", 16 | id: "948807824446742568", 17 | }, 18 | ], 19 | devGuilds: [ 20 | { 21 | name: "Bettle Counter", 22 | id: "1293078905430736928", 23 | }, 24 | ], 25 | betaTestGuilds: [], 26 | cTopic: 27 | "Counting Rules: \n\n1 • No Skipping Numbers\n2 • No Going Back In Numbers\n3 • Must alternate counters (except for solo mode)\n4 • No Botting, Scripting Or Abusing Bugs\n5 • Do Not Intentionally Ruin The Count", 28 | voteUrl: "https://top.gg/bot/1293072781491044415/vote", 29 | logWebhook: process.env.logWebhook, 30 | }; 31 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { ClusterManager, ReClusterManager } = require("discord-hybrid-sharding"); 2 | const { botToken } = require("./config"); 3 | const manager = new ClusterManager("./bot.js", { 4 | token: botToken, 5 | totalShards: "auto", 6 | shardsPerClusters: 2, 7 | totalClusters: "auto", 8 | mode: "process", 9 | }); 10 | 11 | manager.on("clusterCreate", (cluster) => 12 | console.log(`Cluster launched : ${cluster.id}`) 13 | ); 14 | manager.on("clusterDestroy", (cluster) => 15 | console.log(`Cluster destroyed : ${cluster.id}`) 16 | ); 17 | manager.spawn(); 18 | -------------------------------------------------------------------------------- /locales/bn/command.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": { 3 | "success": "> হে <@{{user}}>, আমি ভাষার সেটিংস সফলভাবে {{data}} এ পরিবর্তন করেছি!" 4 | }, 5 | "ping": { 6 | "reply": "> পং! একটু ধৈর্য ধরুন, দেখছি...", 7 | "embed": { 8 | "description": "**এখানে আমার বর্তমান প্রতিক্রিয়া সময়:** `{{ping}}ms`\n\n> ডিসকর্ডের গেটওয়ে পিং: `{{apiPing}}ms`\n> বট চালু আছে: সময় ধরে" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /locales/bn/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "button": { 3 | 4 | }, 5 | "modal": { 6 | 7 | } 8 | } -------------------------------------------------------------------------------- /locales/bn/system.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": { 3 | "autocomplete": { 4 | "fail": "> ওহ না! এই কমান্ড চালাতে গিয়ে কিছু ভুল হয়েছে। দয়া করে আবার চেষ্টা করুন।" 5 | }, 6 | "button": { 7 | "fail": "> ওহ না! এই বোতামটি প্রক্রিয়া করার সময় একটি ত্রুটি ঘটেছে। দয়া করে আবার চেষ্টা করুন।" 8 | }, 9 | "command": { 10 | "fail": "> ওহ না! এই কমান্ড চালাতে একটি ত্রুটি হয়েছে। পরে আবার চেষ্টা করুন।", 11 | "devOnly": "> দুঃখিত! এই কমান্ডটি {{client}} এর ডেভেলপারের জন্য সংরক্ষিত। তাদের সাথে যোগাযোগ করুন সাহায্যের জন্য।", 12 | "underDev": "> খবরদার! এই বটটি বর্তমানে উন্নয়নাধীন। দয়া করে পরে আবার চেক করুন।" 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /locales/en/command.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": { 3 | "success": "> Hey <@{{user}}>, I've successfully updated my language settings to {{data}}!" 4 | }, 5 | "ping": { 6 | "reply": "> Pong! Hold on a moment while I check...", 7 | "embed": { 8 | "description": "**Here's my current response time:** `{{ping}}ms`\n\n> Discord's connection speed: `{{apiPing}}ms`\n> Bot's active time: " 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /locales/en/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "button": { 3 | 4 | }, 5 | "modal": { 6 | 7 | } 8 | } -------------------------------------------------------------------------------- /locales/en/system.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": { 3 | "autocomplete": { 4 | "fail": "> Oops! Something went wrong while trying to run this command. Please try again." 5 | }, 6 | "button": { 7 | "fail": "> Oops! We encountered an error while processing this button. Please try again." 8 | }, 9 | "command": { 10 | "fail": "> Oops! There was an error executing this command. Please try again later.", 11 | "devOnly": "> Sorry! This command is reserved for the developer of {{client}}. Please contact them for assistance.", 12 | "underDev": "> Heads up! This bot is currently under development. Please check back later." 13 | }, 14 | "counting": { 15 | "startCount": "<@{{user}}> You need to start counting from **1**.", 16 | "B2BCount": "**Oh no, <@{{user}}>! You slipped up at {{breakAt}}! \n**You counted **back-to-back**! Don’t worry—let’s restart and keep going strong!", 17 | "wrongNum": "**Oh no, <@{{user}}>! You slipped up at {{breakAt}}!** \nLooks like that was the **wrong number**. Don’t worry—let’s restart and keep going strong!", 18 | "guildSaveUseB2B":"**Hello <@{{user}}>!** You’ve used **1 out of {{totalSave}} server saves**, there’s {{leftSave}} left. You counted **back-to-back**! The next number is **{{nextNum}}**!", 19 | "userSaveUseB2B": "**Hello <@{{user}}>!** You’ve used **1 out of {{totalSave}} your saves**, you have {{leftSave}} left. You counted **back-to-back**! The next number is **{{nextNum}}**!", 20 | "guildSaveUseW":"**Hello <@{{user}}>!** You’ve used **1 out of {{totalSave}} server saves**, there’s {{leftSave}} left. You counted **back-to-back**! The next number is **{{nextNum}}**!", 21 | "userSaveUseW": "**Hello <@{{user}}>!** You’ve used **1 out of {{totalSave}} your saves**, you have {{leftSave}} left. You counted **back-to-back**! The next number is **{{nextNum}}**!", 22 | "numOnlyDisable": "Number Only setting has beed disabled because I dont have `ManageMessages` permission in this channel.", 23 | "embed": { 24 | "des": "Vote **[here]({{link}})** to earn save points, so you can use them next time! For more details, check out /rules." 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /locales/pt/command.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": { 3 | "success": "> Olá <@{{user}}>, atualizei com sucesso as minhas definições de idioma para {{data}}!" 4 | }, 5 | "ping": { 6 | "reply": "> Ping! Aguarde um momento enquanto verifico...", 7 | "embed": { 8 | "description": "**Aqui está o meu tempo de resposta atual:** `{{ping}}ms`\n\n> Velocidade de ligação do Discord: `{{apiPing}}ms`\n> Tempo de atividade do bot: " 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /locales/pt/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "button": {}, 3 | "modal": { 4 | 5 | } 6 | } -------------------------------------------------------------------------------- /locales/pt/system.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": { 3 | "autocomplete": { 4 | "fail": "> Ops! Algo deu errado ao tentar executar este comando. Por favor, tente novamente." 5 | }, 6 | "button": { 7 | "fail": "> Ops! Encontramos um erro ao processar este botão. Por favor, tente novamente." 8 | }, 9 | "command": { 10 | "fail": "> Ops! Ocorreu um erro ao executar este comando. Por favor, tente novamente mais tarde.", 11 | "devOnly": "> Desculpe! Este comando é reservado para o desenvolvedor do {{client}}. Por favor, entre em contato com eles para obter assistência.", 12 | "underDev": "> Atenção! Este bot está atualmente em desenvolvimento. Por favor, volte mais tarde." 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /locales/resources.js: -------------------------------------------------------------------------------- 1 | const command_en = require("./en/command.json"); 2 | const component_en = require("./en/component.json"); 3 | const system_en = require("./en/system.json"); 4 | const command_bn = require("./bn/command.json"); 5 | const component_bn = require("./bn/component.json"); 6 | const system_bn = require("./bn/system.json"); 7 | const command_pt = require("./pt/command.json"); 8 | const component_pt = require("./pt/component.json"); 9 | const system_pt = require("./pt/system.json"); 10 | 11 | const resources = { 12 | en: { 13 | system: system_en, 14 | command: command_en, 15 | component: component_en, 16 | }, 17 | bn: { 18 | system: system_bn, 19 | command: command_bn, 20 | component: component_bn, 21 | }, 22 | pt: { 23 | system: system_pt, 24 | command: command_pt, 25 | component: component_pt, 26 | }, 27 | }; 28 | 29 | module.exports = resources; 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "counting-bot", 3 | "version": "1.0.1", 4 | "main": "index.js", 5 | "scripts": { 6 | "start": "node ./index.js", 7 | "dev": "nodemon ./index.js" 8 | }, 9 | "keywords": [], 10 | "author": "Jason Midul", 11 | "license": "GPL-3.0", 12 | "description": "", 13 | "dependencies": { 14 | "@discordjs/rest": "^2.4.0", 15 | "@top-gg/sdk": "^3.1.6", 16 | "ascii-table": "^0.0.9", 17 | "discord-hybrid-sharding": "^2.2.0", 18 | "discord.js": "^14.16.2", 19 | "dotenv": "^16.4.5", 20 | "express": "^4.21.0", 21 | "i18next": "^23.15.2", 22 | "ioredis": "^5.4.1", 23 | "mathjs": "^13.2.0", 24 | "mongoose": "^8.7.0" 25 | }, 26 | "devDependencies": { 27 | "nodemon": "^3.1.7" 28 | } 29 | } 30 | --------------------------------------------------------------------------------