//#region imports const { Client, Intents } = require('discord.js'); const Discord = require('discord.js'); const { MongoClient, ServerApiVersion, GridFSBucket } = require('mongodb'); const fs = require('fs'); // const OpenAI = require('openai-api') const { Configuration, OpenAIApi } = require("openai"); const Stripe = require('stripe'); const turnManager = require('./commands/turnManager.js'); const { welcome } = require('./commands/admin/welcome.js'); const { handle_interaction, handleContext } = require('./commands/interactionhandler.js'); const { handle_dm } = require('./commands/dm_handler'); const { devCheck } = require('./commands/dev only/devcheck.js'); const { moderation_handler } = require('./commands/admin/moderation.js'); const { registerCommands } = require('./registerCommands.js'); const { backupLists, loadBotBackups } = require('./commands/dev only/backupBot.js'); const { setPresence } = require('./commands/dev only/setPresence.js'); const { exit } = require('process'); const {textToLevels} = require('./commands/Selmer Specific/msgLevels.js'); //#endregion const BASE_LVL_XP = 20; //#region Token area //Adding integration for development mode let token; let IDM = false; let home_server; let debug_channel; let MLAIKEY; let StripeAPIKey; let youtubeAPIKey; if (process.env.token != undefined) { //Use "setx NAME VALUE" in the local powershell terminal to set token = process.env.token; home_server = process.env.home_server; debug_channel = process.env.debug_channel; MLAIKEY = process.env.MLAIKEY; StripeAPIKey = process.env.StripeAPIKey; youtubeAPIKey = process.env.youtubeAPIKey; } else { token = require('./config.json').token; home_server = require('./config.json').home_server; debug_channel = require('./config.json').debug_channel; MLAIKEY = require('./config.json').MLAIKEY; StripeAPIKey = require('./config.json').StripeAPIKey; youtubeAPIKey = require('./config.json').youtubeAPIKey; IDM = token.startsWith("OTI2NT"); } //#endregion const bot = new Client({ intents: [ Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_MESSAGE_REACTIONS, Intents.FLAGS.GUILD_VOICE_STATES, Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS, Intents.FLAGS.GUILD_PRESENCES, Intents.FLAGS.GUILD_MEMBERS, Intents.FLAGS.DIRECT_MESSAGES, Intents.FLAGS.DIRECT_MESSAGE_REACTIONS, Intents.FLAGS.DIRECT_MESSAGE_TYPING, ], partials: [ 'CHANNEL' ] }); const prefix = '!'; bot.prefix = new String; bot.prefix = prefix; bot.inDebugMode = IDM; bot.home_server = home_server; bot.debug_channel = debug_channel; bot.inviteLink = 'https://discord.com/oauth2/authorize?client_id=944046902415093760&scope=applications.commands+bot&permissions=549755289087'; bot.youtubeAPIKey = youtubeAPIKey; const configuration = new Configuration({ apiKey: MLAIKEY, }); bot.openai = new OpenAIApi(configuration); bot.temptext = ''; bot.stripe = Stripe(StripeAPIKey); //The first thing will be an audioPlayer(), the second a queue bot.audioData = new Map(); bot.lockedChannels = new Map(); //#region MongoDB integration //Development support let mongouritemp; if (process.env.MONGODB_URI) { mongouritemp = process.env.MONGODB_URI; } else { mongouritemp = require('./config.json').mongooseURI; } const mongouri = mongouritemp; bot.mongouri = mongouri; const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 }); bot.mongoconnection = client.connect(); //Error stuff var preverr = ""; var errmsg; var errTimes = 1; //#endregion MongoDB Integration end //#region PROCESS STUFF loadBotBackups(bot, IDM); process.on("SIGTERM", (signal) => { console.log(`Process ${process.pid} received a SIGTERM signal`); backupLists(bot, IDM); // process.exit(0); bot.user.setStatus('invisible'); }); process.on("SIGINT", (signal) => { console.log(`Process ${process.pid} has been interrupted`); backupLists(bot, IDM); // process.exit(0); bot.user.setStatus('invisible'); }); process.on('uncaughtException', (signal) => { //Check if this was the last err and if so, ignore if (preverr == signal.stack.toString()) { var tempmsg = errmsg.content; tempmsg.replaceAll(`{${errTimes}}`, `{${errTimes + 1}}`); errTimes++; errmsg.edit(tempmsg); return; } console.log(signal); if (bot.inDebugMode) { return; } const guild = bot.guilds.cache.get(bot.home_server); const owner = guild.members.cache.get(guild.ownerId); preverr = signal.stack.toString(); owner.send(`${owner} SELMER BOT IS DOWN!!!`).then(() => { guild.channels.cache.get("1054550753982828624").send(`<@&944048889038774302> Selmer Bot is down!\n***ERROR STACK:***\n`).then(() => { guild.channels.cache.get("1054550753982828624").send(`\`\`\`${preverr}\`\`\`\nTHIS ERROR HAS OCCURED {1} TIMES IN A ROW`).then((msg) => { errmsg = msg; preverr = signal.stack.toString(); // exit(12); bot.user.setStatus('dnd'); }); }); }); }); //#endregion //#region set up bot commands // const commandFiles = fs.readdirSync('./commands/').filter(file => file.endsWith('.js')); // Obsolete? bot.commands = new Discord.Collection(); const forbiddenFolders = ['db', 'dev only']; //premium, fs.readdirSync('./commands') .forEach(dir => { if (!forbiddenFolders.includes(dir) && !dir.endsWith('.js')) { fs.readdirSync(`./commands/${dir}`) .filter(file => file.endsWith('.js')) .forEach(file => { const command = require(`./commands/${dir}/${file}`); if (command.name && command.description) { bot.commands.set(command.name.toLowerCase(), command); } }); } }); //Set these two manually because all the seperate games can't be included in the command list (all managed by the 'game' file) let temp_command = require("./commands/db/econ.js"); const { STATE } = require('./commands/db/econ.js'); const replies = require('./commands/Selmer Specific/replies.js'); bot.commands.set('econ', temp_command); temp_command = require('./commands/games/game.js'); bot.commands.set('game', temp_command); //Everything in the API should be handled by specific handler functions // const chat = require('./commands/premium/chat.js'); // bot.commands.set('chat', chat); // const stripeCommands = require('./commands/premium/stripe.js'); // bot.commands.set('premium', stripeCommands); // const // bot.commands.set('RSS', ) //#endregion //#region bot.[anything] section //XP Table section let xp_collection = new Map(); let items; var botIsReady = bot.inDebugMode; bot.on('ready', async () => { const startTime = new Date().getTime(); bot.user.setPresence({ activities: [{ name: 'Booting up, please hold!', type: "PLAYING" }], status: 'idle' }); registerCommands(bot).then(() => { //Make then copy the shop bot.mongoconnection.then(client => { const shop = client.db("main").collection("shop"); shop.find().toArray(function(err, itemstemp) { if (err) throw err; items = [...itemstemp]; }); }); //Note the xp numbers are a little wonky on levels 6, 8 and 13 (why though?) //See https://stackoverflow.com/questions/72212928/why-are-the-differences-between-my-numbers-inconsistent-sort-of-compund-interes for (let i = 1; i < 101; i ++) { // xp_collection.set(i, BASE_LVL_XP * .1); let amount = BASE_LVL_XP * (Math.ceil(Math.pow((1.1), (2 * i))) + i); xp_collection.set(i+1, amount); } bot.user.setPresence({ activities: [{ name: '/help', type: 'PLAYING' }], status: 'online' }); if (!bot.inDebugMode) { console.log('SLEEMER BOT ONLINE!!!!! OH MY GOD OH MY GOD!!!'); } else { console.log("Testing testing 1 2 5..."); } //Add the money symbol let srv = bot.guilds.cache.get(bot.home_server).emojis.cache; emj = srv.find((g) => { return g.name == 'selmer_coin' }); bot.currencysymbolmmain = `${emj}`; }).catch((err) => { console.log(err); }).finally(() => { botIsReady = true; console.log(`Setting up Slash Commands took ${(new Date().getTime() - startTime) / 1000} seconds to complete!`); }); }); //Button Section bot.on('interactionCreate', async interaction => { const { commandName } = interaction; if (!botIsReady) { return interaction.reply("The bot is still warming up. This is process can take up to 5 minutes. Please try again in a bit! \:("); } // console.log(bot.lockedChannels); //Slash commands if (interaction.isApplicationCommand()) { if (interaction.isUserContextMenu()) { return handleContext(bot, interaction.options.data[0]); } const logable = ['kick', 'ban', 'unban', 'mute', 'unmute', 'timeout']; const econList = ["buy", 'shop', 'work', 'rank', 'inventory', 'balance', 'sell']; const adminList = ["setpresence", "setactivity"]; if (commandName == "admin" && adminList.includes(interaction.options.data[0].name)) { if (interaction.user.id == bot.guilds.cache.get(bot.home_server).ownerId) { setPresence(bot, interaction); } else { return interaction.reply({ content: "HAHAHAHAHAHAHAHAHAHAHA\n\nno.", ephemeral: true }).catch((err) => { interaction.channel.send({ content: "HAHAHAHAHAHAHAHAHAHAHA\n\nno.", ephemeral: true }); }); } } else if (logable.includes(commandName)) { moderation_handler(bot, interaction, commandName); } else if (econList.includes(commandName)) { bot.commands.get('econ').execute(bot, interaction, Discord, mongouri, items, xp_collection); } else if (commandName == 'game') { if (!bot.inDebugMode) { interaction.reply("This command is still in development, use normal text\nEx: _!game tictactoe @opponent_"); } const command = interaction.options.data[0]; bot.commands.get('game').execute(bot, interaction, command, Discord, mongouri, items, xp_collection); } else if (commandName == 'setup_embed') { const {generateMsg} = require('./commands/admin/easySetup.js') generateMsg(bot, interaction); } else if (bot.commands.has(commandName)) { bot.commands.get(commandName).execute(interaction, Discord, Client, bot); } else { interaction.reply("Unknown command detected!"); } } else { handle_interaction(interaction, mongouri, turnManager, bot, STATE, items, xp_collection); } }); //Add the bot to a server setup bot.on("guildCreate", guild => { if (guild.roles.cache.find((role) => { return (role.name == 'Selmer Bot Commands'); }) == undefined) { guild.roles.create({ name: 'Selmer Bot Commands' }); } if (guild.roles.cache.find((role) => { return (role.name == 'Selmer Bot Calendar'); }) == undefined) { guild.roles.create({ name: 'Selmer Bot Calendar' }); } //const role = guild.roles.cache.find((role) => role.name === 'Selmer Bot Mod'); // member.roles.cache.has('role-id-here'); const server = bot.guilds.cache.get(guild.id); server.members.fetch(guild.ownerId).then(function(owner) { owner.send('Thank you for adding Selmer Bot to your server!\nPlease give people you want to have access to Selmer Bot\'s restricted commands the "_Selmer Bot Commands_" role and people you want to access set the calendar the "_Selmer Bot Calendar_" role'); owner.send('To help set up Selmer Bot to work better with your server, use _/setup help_ in a channel Selmer Bot is in!'); }); //Set up the server bot.mongoconnection.then(client => { const dbo = client.db(guild.id).collection('SETUP'); dbo.insertMany([{_id: 'WELCOME', 'welcomechannel': null, 'welcomemessage': null, 'welcomebanner': null, 'col': "#FFFFFF"}, {_id: 'LOG', 'keepLogs': false, 'logchannel': null, 'severity': 0}, {_id: 'announcement', channel: null, role: null}, {_id: 'roles', commands: ["Selmer Bot Commands"], announcements: "Selmer Bot Calendar"}]); }); }); bot.on("guildDelete", guild => { bot.mongoconnection.then((client) => { //Insufficient Permission???? // db.dropDatabase(); try { const db = client.db(guild.id); db.listCollections().forEach(function(x) { db.collection(x.name).drop(); }); var times; const dbo = client.db('main').collection('reminderKeys'); //ReminderKeys are all stored as userId, the reminders themselves are not dbo.findOne({userId: guild.id}).then((doc) => { if (!doc || !doc.times) { return; } times = doc.times; const tbo = client.db('main').collection('reminders'); tbo.find({time: {$in: times}}).toArray((err, docs) => { try { for (let i = 0; i < docs.length; i ++) { for (let j in docs[i]) { if (!isNaN(j) && (docs[i][j].guildId == guild.id)) { delete docs[i][j]; docs[i].amt --; } } if (docs.amt > 0) { tbo.replaceOne({ time: docs[i].time }, docs[i]); } else { tbo.deleteOne({ time: docs[i].time }); } } } catch (err) { console.error(err); } }); }); dbo.deleteOne({ userId: guild.id }); } catch (err) { console.error(err); } }) }); //Welcome new members bot.on('guildMemberAdd', async (member) => { if (member.guild.id == bot.home_server && !bot.inDebugMode) { return; } //Check for impartial data if (member.partial) { member = await member.fetch(); } const guild = bot.guilds.cache.get(member.guild.id); bot.mongoconnection.then(client => { const dbo = client.db(member.guild.id).collection('SETUP'); dbo.findOne({_id: 'WELCOME'}).then(async (doc) => { if (!doc) { return; } var welcomechannel; if (doc.welcomechannel == null) { welcomechannel = guild.channels.cache.find(channel => channel.name.toLowerCase() === 'welcome'); } else { welcomechannel = guild.channels.cache.get(doc.welcomechannel) } if (welcomechannel == null) { return; // console.log('No welcome channel detected'); } await welcome(member, welcomechannel, doc.welcomemessage, doc.welcomebanner, (doc.welcometextcolor) ? doc.welcometextcolor : "#FFFFFF"); }); }) }); bot.on('messageCreate', (message) => { //DM SECTION if (message.channel.type === "DM") { return handle_dm(message, bot); } else if (message.content.indexOf('!spam_collection') != -1) { //Handle spam collection/Dev commands return devCheck(message, bot); } else if (message.type === "CHANNEL_PINNED_MESSAGE") { //Debug log stuff if (message.guild.id == bot.home_server && message.channel.id == bot.debug_channel) { message.delete(); } } //Special case, testing server (still need the emojis and error logging) if (!bot.inDebugMode && message.guild.id == bot.home_server) { return; } if (message.mentions.has(bot.user.id)) { if (message.content == `<@${bot.user.id}>`) { return message.reply("What?"); } else { return replies(bot, message); } } //Check if the prefix exists if (message.author.bot) { return; } if (message.content.startsWith(prefix)) { //Game section (too complicated to move to Slash Commands) //Note: Slash commands do not register as valid replies const args = message.content.slice(prefix.length).split(' '); const command = args.shift().toLowerCase(); if (command == 'game' || command == 'accept') { bot.commands.get(command).execute(bot, message, args, command, Discord, mongouri, items, xp_collection); } else if (command == 'rss' && bot.inDebugMode) { const rss = require('./side projects/RSSHandlers/rssFeed.js'); rss.execute(message, args, Discord, client, bot); } else { textToLevels(bot, message, xp_collection); } } else { //Use for the leveling-by-interaction system textToLevels(bot, message, xp_collection); } }); //#endregion //Last Line(s) bot.login(token);