From 3d864526b61b216c8b99332eaaf8f2111e56b1d9 Mon Sep 17 00:00:00 2001 From: Kyle S Date: Fri, 8 Mar 2024 15:50:27 -0500 Subject: [PATCH 1/6] Removing unnecessary v2 files --- .axoncorerc.json | 1 - .eslintignore | 1 - .eslintrc.json | 6 - .gitignore | 16 +- README.md | 24 --- Util/gqlHelper.js | 19 -- Util/wordlefunctions.js | 375 ------------------------------------ configs/config.json | 64 ------ configs/lang.json | 9 - configs/secret.json.example | 6 - data.csv | 3 - scripts/GenerateHotlines.py | 26 --- 12 files changed, 5 insertions(+), 545 deletions(-) delete mode 100644 .axoncorerc.json delete mode 100644 .eslintignore delete mode 100644 .eslintrc.json delete mode 100644 README.md delete mode 100644 Util/gqlHelper.js delete mode 100644 Util/wordlefunctions.js delete mode 100644 configs/config.json delete mode 100644 configs/lang.json delete mode 100644 configs/secret.json.example delete mode 100644 data.csv delete mode 100644 scripts/GenerateHotlines.py diff --git a/.axoncorerc.json b/.axoncorerc.json deleted file mode 100644 index 2a8a8f7e..00000000 --- a/.axoncorerc.json +++ /dev/null @@ -1 +0,0 @@ -{"library":"eris"} diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index f59ec20a..00000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -* \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index bf89e8a5..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "@axonteam/eslint-config", - "rules": { - "no-magic-numbers": "off" - } -} diff --git a/.gitignore b/.gitignore index 83decfa7..1338141b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,9 @@ -.DS_Store +build node_modules + +*.vscode *.code-workspace -package-lock.json -configs/secret.json -src/index.js -src/modules/Info/listeners/index.js -.env -# Local directories/files -Archives -src/modules/Games/data.csv -src/assets/wordle/wordledata.csv +.env -.npmrc +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index a1b21c5c..00000000 --- a/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Naga -Naga is a multi-purpose bot created for the Avatar: The Last Airbender Discord community. It is written in JavaScript using the [eris](https://github.com/abalabahaha/eris) library. The framework used is [AxonCore](https://github.com/KhaaZ/axoncore), which is currently abandoned by its original authors. Naga currently uses its own fork of AxonCore located [here](https://github.com/eaobao/axoncore). - -## Features -Naga has a relatively wide range of features that is constantly growing based on our needs. - -- Automatic banner updates -- Automatic member birthday announcements -- Economy -- Moderation -- Server archives -- A variety of fun and engaging commands such as topics and would-you-rather -- And more! - -## Setup Guide -`configs/config.json` is pre-configured with permissions. The server prefix is stored in our database, but this file is also where you can change the dev and admin prefixes for your dev environment. Another useful value I recommend enabling is "debug mode". When enabled, your bot's status will change to Do Not Disturb, and all commands will become unusable for normal members. This also provides you with more useful logs in your console. - -`configs/secret.json` is (currently) where you enter your bot token. You can use the format in secret.json.example as a starting point. Soon, this will change to environment variables. - -[Naga's Trello page](https://trello.com/b/Mx9bdabJ/features-issues) - -## Contributions - -Any and all contributions to Naga are welcome (if you have access to this readme, you are a contributor!) Feel free to look at Naga's Trello page to view our roadmap, and ask any questions you may have in #dev_hub! \ No newline at end of file diff --git a/Util/gqlHelper.js b/Util/gqlHelper.js deleted file mode 100644 index 42d17491..00000000 --- a/Util/gqlHelper.js +++ /dev/null @@ -1,19 +0,0 @@ -const requireText = require('require-text'); -module.exports = { - - - -//GraphiQl Templates - NextAirDate_NoQuery: requireText('../src/assets/gql/NextAirDateWithoutQuery.graphql', require) -, NextAirDate_Query: requireText('../src/assets/gql/NextAirDateWithQuery.graphql', require) -, Schedule: requireText('../src/assets/gql/Schedule.graphql', require) -, Seiyuu: requireText('../src/assets/gql/Seiyuu.graphql', require) -, Watching: requireText('../src/assets/gql/Watching.graphql', require) - - - - - - -// End of expor -} \ No newline at end of file diff --git a/Util/wordlefunctions.js b/Util/wordlefunctions.js deleted file mode 100644 index f5ca54a4..00000000 --- a/Util/wordlefunctions.js +++ /dev/null @@ -1,375 +0,0 @@ -const fs = require('fs'); -var csv = require('jquery-csv'); -const Canvas = require('canvas'); -const answers = require('../src/assets/wordle/answers.json') - -function GetAnswer() -{ - //randomly select answer from list of 600 words - var j = Math.floor(Math.random() * answers.length); - return answers[j].toUpperCase(); -} - -const ValidGuess = (guess) => -{ - if(guess=== undefined){return false;} - else if (guess.length!=5){return false;} - //else if (!answers.includes(guess.toUpperCase())){return false;} - else{return true;} -}; - -const PlayedToday = (dateData) => -{ - if(GetTodaysDate()==dateData){return true;} - else{return false;} -}; -const GetTodaysDate = () => -{ - var today = new Date(); - var dd = String(today.getDate()).padStart(2, '0'); - var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0! - var yyyy = today.getFullYear(); - return mm + '/' + dd + '/' + yyyy; -}; -const AddSpace = (data) => -{ - if(data.length==0){return "";} - else{return " ";} -}; -const GetImage = (guessLetter,answerLetter, i) => -{ - //letter is in word at same spot - if(guessLetter === undefined){return 0;} - //letter is in word at same spot - else if(guessLetter.charAt(i)==answerLetter.charAt(i)){return 1;} - //letter is in word at different spot - else if(answerLetter.includes(guessLetter.charAt(i))){return 2;} - //letter is not in word - else{return 3;} -}; -function writeToCSVFile(newData) -{ - const filename = 'data.csv'; - let csvContent = "data:text/csv;charset=utf-8," - + newData.map(e => e.join(",")).join("\n"); - - fs.writeFile(filename, csvContent, err => - { - if (err) { - console.log('Error writing to csv file', err); - } else { - console.log(`saved as ${filename}`); - } - }); -} -async function LoadGame(msg, guesses, answer) -{ - const canvas = Canvas.createCanvas(330, 397); - const context = canvas.getContext('2d'); - - const background = await Canvas.loadImage('../src/assets/wordle/images/BlankImage.png'); - context.drawImage(background, 0, 0, canvas.width, canvas.height); - - context.font = '42px Clear Sans, Helvetica Neue, Arial, sans-serif'; - context.textAlign = 'center' - context.fillStyle = '#d7dadc'; - - const absentSquare = await Canvas.loadImage('../src/assets/wordle/images/ColorAbsent.png') - const emptySquare = await Canvas.loadImage('../src/assets/wordle/images/EmptySquare.png'); - const greenSquare = await Canvas.loadImage('../src/assets/wordle/images/GreenSquare.png'); - const yellowSquare = await Canvas.loadImage('../src/assets/wordle/images/YellowSquare.png'); - let square = absentSquare; - - let squareSize = 62; - let rowOffset = 0; - let buffer = 0; - - for (let j = 0; j < 6; j++) - { - for (let i = 0; i < 5; i++) - { - const imageNumber = GetImage(guesses[j],answer,i); - - if(imageNumber==0){square = emptySquare;} - else if(imageNumber==1){square = greenSquare;} - else if(imageNumber==2){square = yellowSquare;} - else if(imageNumber==3){square = absentSquare;} - - context.drawImage(square, i*squareSize+buffer, rowOffset, squareSize, squareSize); - if(guesses[j] != undefined) - { - context.fillText(guesses[j].charAt(i), (squareSize/2)+buffer+squareSize*i, rowOffset+42); - } - - buffer+=5; - } - buffer=0; - rowOffset+=squareSize+5; - } - - this.sendMessage({ - file: [{ - file: canvas.toBuffer(), - name: 'wordle.png' - }] - }) - -} -async function Guess(msg,guesses,newGuess, answer) -{ - const canvas = Canvas.createCanvas(330, 397); - const context = canvas.getContext('2d'); - const background = await Canvas.loadImage('../src/assets/wordle/images/BlankImage.png'); - context.drawImage(background, 0, 0, canvas.width, canvas.height); - - context.font = '42px Clear Sans, Helvetica Neue, Arial, sans-serif'; - context.textAlign = 'center' - context.fillStyle = '#d7dadc'; - - //for debuging purposes - console.log("Answer " + answer); - console.log("newGuess " + newGuess); - - const absentSquare = await Canvas.loadImage('../src/assets/wordle/images/ColorAbsent.png'); - const emptySquare = await Canvas.loadImage('../src/assets/wordle/images/EmptySquare.png'); - const greenSquare = await Canvas.loadImage('../src/assets/wordle/images/GreenSquare.png'); - const yellowSquare = await Canvas.loadImage('../src/assets/wordle/images/YellowSquare.png'); - let square = absentSquare; - - let squareSize = 62; - let rowOffset = 0; - let buffer = 0; - - if(guesses=="") - { - guesses[0] = newGuess; - } - else - { - guesses.push(newGuess); - } - - for (let j = 0; j < 6; j++) - { - for (let i = 0; i < 5; i++) - { - const imageNumber = GetImage(guesses[j],answer,i); - - if(imageNumber==0){square = emptySquare;} - else if(imageNumber==1){square = greenSquare;} - else if(imageNumber==2){square = yellowSquare;} - else if(imageNumber==3){square = absentSquare;} - - context.drawImage(square, i*squareSize+buffer, rowOffset, squareSize, squareSize); - if(guesses[j] != undefined) - { - context.fillText(guesses[j].charAt(i), (squareSize/2)+buffer+squareSize*i, rowOffset+45); - } - - buffer+=5; - } - - buffer=0; - rowOffset+=squareSize+5; - } - - this.sendMessage({ - file: [{ - file: canvas.toBuffer(), - name: 'wordle.png' - }] - }) -} -function LoadNewWordle(msg) -{ - fs.readFile('data.csv', 'UTF-8', (err, fileContent) => - { - if (err) { console.log(err) } - csv.toArrays(fileContent, {}, (err, data) => - { - if (err) { console.log(err) } - console.log(data.length); - if(data.length==0) - { - console.log("no data"); - data[0]=[ - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'user', - 'wordOfTheDay', - 'canGuess', - 'lastGuessDate', - 'guesses', - 'wins', - 'games', - 'hasCompletedToday' - ]; - } - console.log(data.length); - - for (let i = 1, len = data.length; i < len; i++) - { - console.log(data[i]); - if(data[i][0]==msg.author.id) - { - if(PlayedToday(data[i][3])) - { - if(!msg.content.includes("ISAIDSTARTANEWGAME")) - { - msg.channel.createMessage("You have already played a game, please come back tomorrow"); - return; - } - } - - //GetNewAnswer - data[i][1] = GetAnswer(); - //update date - data[i][3] = GetTodaysDate(); - //clear guesses - data[i][4] = ""; - //remove game completion status - data[i][8] = false; - - //update csv file - writeToCSVFile(data); - LoadGame(msg,data[i][4],data[i][1]); - return; - } - } - - //create new entry - data.push([msg.author.id,GetAnswer(),'false',GetTodaysDate(),,0,0,0,false]); - writeToCSVFile(data); - LoadGame(msg,"",""); - })}) -} -function PlayWordle(msg) -{ - fs.readFile('data.csv', 'UTF-8', (err, fileContent) => - { - if (err) { console.log(err) } - csv.toArrays(fileContent, {}, (err, data) => - { - if (err) { console.log(err) } - if(data.length==0) - { - data[0]=[ - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'data:text/csv;charset=utf-8', - 'user', - 'wordOfTheDay', - 'canGuess', - 'lastGuessDate', - 'guesses', - 'wins', - 'games', - 'hasCompletedToday' - ]; - } - for (let i = 1, len = data.length; i < len; i++) - { - - if(data[i][0]==msg.author.id) - { - if(data[i][8]=="true") - { - msg.channel.createMessage("You have already completed a game today, Come back tomorrow"); - return; - } - - var guess = msg.content.split(" ")[1]; - - //Guess checks - if(!ValidGuess(guess)) - { - msg.channel.createMessage("Guesses must be a valid 5 letter word "); - return; - } - - //clean data and update file - var guesses = data[i][4].split(" "); - guess = guess.toUpperCase(); - data[i][4] = data[i][4] +AddSpace(data[i][4])+ guess; - writeToCSVFile(data); - - Guess(msg,guesses, guess, data[i][1]); - - //check to see if guess and answer match - for (var c=0; c - { - if (err) { console.log(err) } - csv.toArrays(fileContent, {}, (err, data) => - { - if (err) { console.log(err) } - for (let i = 1, len = data.length; i < len; i++) - { - if(data[i][0]==msg.author.id) - { - var wins = data[i][6]; - var games = data[i][7]; - var result = Math.round((wins / games) * 100); - - msg.channel.createMessage("Stats: \nPlayed : "+games+"\nWin % : "+result); - - return; - } - } - - data[data.length] = [msg.author.id,'','false','','','0','0'] - msg.channel.createMessage("Stats: \nPlayed : "+0+"\nWin % : "+0); - writeToCSVFile(data); - })}) -} - -module.exports = { LoadNewWordle, PlayWordle, ShowWordleStats}; \ No newline at end of file diff --git a/configs/config.json b/configs/config.json deleted file mode 100644 index d2fb28a0..00000000 --- a/configs/config.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "settings": { - "lang": "english", - "debugMode": false, - "library": 0, - "logger": 1, - "db": 2, - "guildConfigCache": 1000, - "joinLogs": false - }, - - "prefixes": { - "general": "n.", - "admin": "na.", - "owner": "$" - }, - "emotes": { - "error": "<:no:917982868922335272>", - "success": "<:yes:917982955362734100>" - }, - "info": { - "name": "Naga", - "description": "naga v2", - "version": "2.0.0" - }, - "staff": { - "owners": [ - { "name": "TwoDog", "id": "123261299864895489" }, - { "name": "soda", "id": "254814547326533632" } - ], - "admins": [ - { "name": "Jules_Zules", "id": "737542058138533950" }, - { "name": "kra2os", "id": "222634331384840193" }, - { "name": "airaloriel", "id": "237413954220064770" }, - { "name": "hcnebun", "id": "510193587430883328" } - - ], - "sentries": [ - { "name": "oppatwunk", "id": "791269922617556995" }, - { "name": "garythesnale", "id": "890752824609230959" }, - { "name": "1nterlnked", "id": "416630867209748483" }, - { "name": "ashtonabendstern", "id": "681334177928577066" }, - { "name": "helluvafizz", "id": "784224468116701195" }, - { "name": "chayt", "id": "596364946045141005" } - ], - "dailis": [ - { "name": "pwr_mtlbndr", "id": "449227153905680386" }, - { "name": "asan", "id": "169880952804212736" }, - { "name": "helena", "id": "1144231168242368532" } - ] - }, - "template": { - "embeds": { - "help": 9031664, - "global": 9031664, - "error": 15844367 - }, - - "emotes": { - "error": "<:no:917982868922335272>", - "success": "<:yes:917982955362734100>" - } - } -} diff --git a/configs/lang.json b/configs/lang.json deleted file mode 100644 index 1548f4dc..00000000 --- a/configs/lang.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "english": { - "ERR_BOT_PERM": "I don't have sufficient permissions to do that! I need: {{permissions}}", - "ERR_CALLER_PERM": "You don't have sufficient permissions to do that! You need: {{permissions}}", - "ERR_DESTINATION_PERM": "This user is a mod/admin, I can't do that!", - "ERR_COOLDOWN": "Please, slow down! Remaining time: **{{cooldown}}** sec...", - "ERR_GENERAL": "An unexpected error occured! Contact the bot developers." - } -} \ No newline at end of file diff --git a/configs/secret.json.example b/configs/secret.json.example deleted file mode 100644 index 3e4bd335..00000000 --- a/configs/secret.json.example +++ /dev/null @@ -1,6 +0,0 @@ -{ - "bot": { - "token": "joe mama", - "logChannel": "1234567890" - } -} diff --git a/data.csv b/data.csv deleted file mode 100644 index 40ca878d..00000000 --- a/data.csv +++ /dev/null @@ -1,3 +0,0 @@ -data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,data:text/csv;charset=utf-8,user,wordOfTheDay,canGuess,lastGuessDate,guesses,wins,games,hasCompletedToday -285136042464641231,TASTY,true,1/11/2022,pizza,3,4 -285136042464641232,WHILE,false,01/18/2022,LATER DOUBT HELPS KILNS MILER WHILE,0,21,31,true \ No newline at end of file diff --git a/scripts/GenerateHotlines.py b/scripts/GenerateHotlines.py deleted file mode 100644 index 9d6bd544..00000000 --- a/scripts/GenerateHotlines.py +++ /dev/null @@ -1,26 +0,0 @@ -import csv -import json - -def convertToJson(csvFilePath, jsonFilePath): - data = {} - - csvFile = open(csvFilePath, encoding='utf-8') - jsonFile = open(jsonFilePath, 'w', encoding='utf-8') - - csvReader = csv.DictReader(csvFile) - - jsonFile.write('[\n') - - for row in csvReader: - json.dump(row, jsonFile, indent=4) - if (row['COUNTRY_CODE'] == 'ZW'): - jsonFile.write('\n') - else: - jsonFile.write(',\n') - - jsonFile.write(']') - -csvFilePath = r'scripts\CrisisHotlines.csv' -jsonFilePath = r'src\assets\Hotlines.json' - -convertToJson(csvFilePath, jsonFilePath); \ No newline at end of file From 3232b927892ec8ea4629ece69aafa8d24edc33c5 Mon Sep 17 00:00:00 2001 From: Kyle S Date: Wed, 20 Mar 2024 19:25:26 -0400 Subject: [PATCH 2/6] Added command and module handlers. Database WIP --- src/Core/Logger.ts | 52 ++++++++++++ src/Core/Naga.ts | 25 ++++-- src/Managers/CommandManager.ts | 35 ++++++++ src/Managers/ModuleManager.ts | 66 +++++++++++++++ src/Managers/PermissionsManager.ts | 1 + src/Modules/Info/commands/Test.ts | 23 +++++ src/Modules/Info/commands/index.ts | 1 + src/Modules/Info/index.ts | 11 +++ src/Modules/Misc/commands/Ping.ts | 7 +- src/Modules/Misc/commands/index.ts | 2 +- src/Modules/Misc/index.ts | 2 +- src/Modules/index.ts | 3 +- src/Structures/Base.ts | 27 +++++- src/Structures/Collection.ts | 41 +++++++++ src/Structures/Command.ts | 31 ++++++- src/Structures/EventCollection.ts | 69 +++++++++++++++ src/Structures/ICommand.ts | 14 ++++ src/Structures/Module.ts | 12 ++- src/Structures/Utils.ts | 9 +- src/Structures/index.ts | 7 ++ src/index.d.ts | 130 +++++++++++++++++++++++++++++ 21 files changed, 547 insertions(+), 21 deletions(-) create mode 100644 src/Core/Logger.ts create mode 100644 src/Managers/CommandManager.ts create mode 100644 src/Managers/ModuleManager.ts create mode 100644 src/Modules/Info/commands/Test.ts create mode 100644 src/Modules/Info/commands/index.ts create mode 100644 src/Modules/Info/index.ts create mode 100644 src/Structures/Collection.ts create mode 100644 src/Structures/EventCollection.ts create mode 100644 src/Structures/ICommand.ts create mode 100644 src/Structures/index.ts create mode 100644 src/index.d.ts diff --git a/src/Core/Logger.ts b/src/Core/Logger.ts new file mode 100644 index 00000000..397cbc54 --- /dev/null +++ b/src/Core/Logger.ts @@ -0,0 +1,52 @@ +import path from 'path'; + +export default class Logger { + private errorRegex = /\((.*):(\d+):(\d+)\)$/; + + public fileName(file: any): string { + return path.parse(file).name; + } + + public callerLocation(): any { + const e: any = new Error(); + const regex = /\((.*):(\d+):(\d+)\)$/; + const match: any = regex.exec(e.stack.split("\n")[2]); + return match[1]; + } + + public info(message: string, prefix?: string) { + if (prefix === undefined) { + const e: any = new Error(); + const match: any = this.errorRegex.exec(e.stack.split("\n")[2]); + prefix = this.fileName(match[0]) + } + return console.info(`[${prefix}] ${message}`); + } + + public debug(message: string, prefix?: string) { + if (prefix === undefined) { + const e: any = new Error(); + const match: any = this.errorRegex.exec(e.stack.split("\n")[2]); + prefix = this.fileName(match[0]) + } + return console.debug(`[${prefix}] ${message}`); + } + + public error(message: Error|string, prefix?: string) { + if (prefix === undefined) { + const e: any = new Error(); + const match: any = this.errorRegex.exec(e.stack.split("\n")[2]); + prefix = this.fileName(match[0]) + } + return console.error(`[${prefix}] ${message}`); + } + + public log(message: string, prefix?: string) { + if (prefix === undefined) { + const e: any = new Error(); + const match: any = this.errorRegex.exec(e.stack.split("\n")[2]); + prefix = this.fileName(match[0]) + } + return console.log(`[${prefix}] ${message}`); + } +} \ No newline at end of file diff --git a/src/Core/Naga.ts b/src/Core/Naga.ts index 5d940097..7b510762 100644 --- a/src/Core/Naga.ts +++ b/src/Core/Naga.ts @@ -1,15 +1,21 @@ import * as djs from 'discord.js'; -import Utils from '../Structures/Utils'; +import Logger from './Logger'; +import { Utils } from '../Structures'; import PermissionsManager from '../Managers/PermissionsManager'; +import CommandManager from '../Managers/CommandManager'; +import ModuleManager from '../Managers/ModuleManager'; export default class Naga { - private _client!: djs.Client; + private _client: djs.Client; public isReady: boolean; public _utils: Utils; + public _logger: Logger; public permissions: PermissionsManager; + public commands: CommandManager; + public modules: ModuleManager; constructor() { this.isReady = false; @@ -23,6 +29,10 @@ export default class Naga { return this._utils; } + public get logger() { + return this._logger; + } + /** * Initializes the bot client * @param opts Client options @@ -33,15 +43,20 @@ export default class Naga { options.allowedMentions = opts.allowedMentions || { parse: [ 'roles'] }; options.intents = opts.intents || [djs.GatewayIntentBits.Guilds]; + this._logger = new Logger(); + this._utils = new Utils(); + // Initialize the discord.js client this._client = new djs.Client(options); // Initialize manager classes this.permissions = new PermissionsManager(this); + this.commands = new CommandManager(this); + this.modules = new ModuleManager(this); // Event functions this.client.once('ready', this.ready.bind(this)); - this.client.on('error', err => console.error(err)) + this.client.on('error', err => this.logger.error(err)) // Connect to Discord const token = process.env.TOKEN as string; @@ -54,7 +69,7 @@ export default class Naga { */ public async login(t: string): Promise { const token = 'Bot ' + t; - console.info('Connecting to Discord...'); + this.logger.info('Connecting to Discord...', 'BotClient'); await this.client.login(token); } @@ -71,7 +86,7 @@ export default class Naga { * Executes when the bot fires the "ready" event */ public async ready(): Promise { - console.log(`Successfully connected to Discord as ${this.client.user!.username}`) + this.logger.info(`Successfully connected to Discord as ${this.utils.fullName(this.client.user)}`, 'BotClient'); this.isReady = true; await this.editStatus({name: 'Testing', type: 0}) diff --git a/src/Managers/CommandManager.ts b/src/Managers/CommandManager.ts new file mode 100644 index 00000000..9609512a --- /dev/null +++ b/src/Managers/CommandManager.ts @@ -0,0 +1,35 @@ +import { Client } from 'discord.js'; +import Naga from '../Core/Naga'; +import { EventCollection } from '../Structures'; + +export default class CommandManager extends EventCollection { + public naga: Naga; + public _client: Client; + + constructor(naga: Naga) { + super(); + this.naga = naga; + this._client = naga.client; + } + + public get logger() { + return this.naga.logger; + } + + /** + * Loads commands into the database + * @param Command Command class instance + * @returns + */ + public register(Command: any): void { + if (Object.getPrototypeOf(Command).name !== 'Command') { + return this.logger.debug('Skipping unknown command'); + } + + let command = new Command(this.naga); + + command.name = command.aliases[0]; + + this.logger.info(`Registering command ${command.name}`); + } +} \ No newline at end of file diff --git a/src/Managers/ModuleManager.ts b/src/Managers/ModuleManager.ts new file mode 100644 index 00000000..c438c3d8 --- /dev/null +++ b/src/Managers/ModuleManager.ts @@ -0,0 +1,66 @@ +import { Client } from 'discord.js'; +import Naga from '../Core/Naga'; +import { Collection, Command, Module } from '../Structures'; +const each = require('async-each'); + +import * as modules from '../Modules'; + +export default class ModuleManager extends Collection { + public naga: Naga; + public _client: Client; + public moduleList: Module[]; + + constructor(naga: Naga) { + super(); + this.naga = naga; + this._client = naga.client; + this.loadModules(); + } + + public get logger() { + return this.naga.logger; + } + + /** + * Loads all modules from the modules directory + */ + public loadModules(): void { + for (const Value of Object.values(modules)) { + const module = new Value(this.naga); + try { + this.register(module); + } catch (e) { + this.logger.error(__filename, e); + } + } + + this.logger.info(`Registered ${Object.keys(modules).length} modules.`) + } + + /** + * Loads modules into the database. + * @param module Module class instance + * @returns + */ + public register(module: any): void { + if (!(module instanceof Module)) { + return this.logger.error(`${module} is an invalid module.`); + } + + //@ts-ignore + let activeModule = this.get(module.name); + + if (activeModule) { + // to-do + } + + this.logger.debug(`Registering module ${module.name}.`); + + if (module.commands) { + const commands = Array.isArray(module.commands) ? module.commands : Object.values(module.commands); + each(commands, (command: Command) => this.naga.commands.register(command)); + } + + module.ensureInterface(); + } +} \ No newline at end of file diff --git a/src/Managers/PermissionsManager.ts b/src/Managers/PermissionsManager.ts index 31fd3509..f3622849 100644 --- a/src/Managers/PermissionsManager.ts +++ b/src/Managers/PermissionsManager.ts @@ -15,6 +15,7 @@ export default class PermissionsManager { */ public isDeveloper(user: djs.User): boolean { if (!user) return false; + return true; } /** diff --git a/src/Modules/Info/commands/Test.ts b/src/Modules/Info/commands/Test.ts new file mode 100644 index 00000000..0ca64ffc --- /dev/null +++ b/src/Modules/Info/commands/Test.ts @@ -0,0 +1,23 @@ +import { Message } from 'discord.js'; + +import Command from '../../../Structures/Command'; +import CommandMetadata from '../../../Structures/CommandMetadata'; + +export class Test extends Command { + + public aliases : string[] = ['test']; + public description : string = 'Displays the ping between Naga and the Discord API'; + public usage : string = 'test'; + public example : string = 'test'; + public cooldown : number = 5000; + public expectedArgs : number = 0; + + public execute({ message, t}: CommandMetadata) { + let start = Date.now(); + + return this.sendMessage(message.channel, 'Pong!').then((msg: Message) => { + let diff = (Date.now() - start); + return msg.edit(`Pong! \`${diff}ms\``); + }) + } +} \ No newline at end of file diff --git a/src/Modules/Info/commands/index.ts b/src/Modules/Info/commands/index.ts new file mode 100644 index 00000000..8638d571 --- /dev/null +++ b/src/Modules/Info/commands/index.ts @@ -0,0 +1 @@ +export {Test} from './Test'; \ No newline at end of file diff --git a/src/Modules/Info/index.ts b/src/Modules/Info/index.ts new file mode 100644 index 00000000..f1820527 --- /dev/null +++ b/src/Modules/Info/index.ts @@ -0,0 +1,11 @@ +import Module from '../../Structures/Module'; +import * as commands from './commands'; + +export default class Info extends Module { + public module : string = 'Info'; + public description : string = 'Info commands'; + public enabled : boolean = true; + public commands : {} = commands; + + public start() {}; +} \ No newline at end of file diff --git a/src/Modules/Misc/commands/Ping.ts b/src/Modules/Misc/commands/Ping.ts index be74a685..047e3943 100644 --- a/src/Modules/Misc/commands/Ping.ts +++ b/src/Modules/Misc/commands/Ping.ts @@ -1,11 +1,12 @@ +'use-strict'; + import { Message } from 'discord.js'; import Command from '../../../Structures/Command'; import CommandMetadata from '../../../Structures/CommandMetadata'; -export default class Ping extends Command { - - public aliases : string[] = ['ping']; +export class Ping extends Command { + public aliases : string[] = ['ping']; public description : string = 'Displays the ping between Naga and the Discord API'; public usage : string = 'ping'; public example : string = 'ping'; diff --git a/src/Modules/Misc/commands/index.ts b/src/Modules/Misc/commands/index.ts index 8a4e32df..b0564f48 100644 --- a/src/Modules/Misc/commands/index.ts +++ b/src/Modules/Misc/commands/index.ts @@ -1 +1 @@ -export {default as Ping} from './Ping'; \ No newline at end of file +export {Ping} from './Ping'; \ No newline at end of file diff --git a/src/Modules/Misc/index.ts b/src/Modules/Misc/index.ts index bdefe5c9..8fe4d94e 100644 --- a/src/Modules/Misc/index.ts +++ b/src/Modules/Misc/index.ts @@ -2,7 +2,7 @@ import Module from '../../Structures/Module'; import * as commands from './commands'; export default class Misc extends Module { - public module : string = 'Misc'; + public module : string = 'Misc'; public description : string = 'Miscellaneous commands'; public enabled : boolean = true; public commands : {} = commands; diff --git a/src/Modules/index.ts b/src/Modules/index.ts index cdf00f48..48b328c6 100644 --- a/src/Modules/index.ts +++ b/src/Modules/index.ts @@ -1 +1,2 @@ -export {default as Misc} from './Misc'; \ No newline at end of file +export { default as Info } from './Info'; +export { default as Misc } from './Misc'; \ No newline at end of file diff --git a/src/Structures/Base.ts b/src/Structures/Base.ts index ac1b2357..c65a10cb 100644 --- a/src/Structures/Base.ts +++ b/src/Structures/Base.ts @@ -6,7 +6,7 @@ export default class Base { public guild!: djs.Guild; private _naga: Naga; - constructor(naga: Naga, guild: djs.Guild) { + constructor(naga: Naga, guild?: djs.Guild) { this._naga = naga; if (guild) { @@ -14,7 +14,6 @@ export default class Base { } } - /** * The Naga instance */ @@ -33,6 +32,30 @@ export default class Base { return this.naga.utils; } + public toJSON(): object { + const copy: {[key: string]: any} = {}; + + for (const key in this) { + if (!this.hasOwnProperty(key) || key.startsWith('_')) { + continue; + } + + if (!this[key]) { + copy[key] = this[key]; + } else if (this[key] instanceof Set) { + copy[key] = Array.from(this[key]); + } else if (this[key] instanceof Map) { + copy[key] = Array.from((this[key]).values()); + } else if ((this[key]).toJSON != undefined) { + copy[key] = (this[key]).toJSON(); + } else { + copy[key] = this[key]; + } + } + + return copy; + } + /** * Checks if user is a developer * @param user A User object diff --git a/src/Structures/Collection.ts b/src/Structures/Collection.ts new file mode 100644 index 00000000..ea47622d --- /dev/null +++ b/src/Structures/Collection.ts @@ -0,0 +1,41 @@ +/** + * Generic collection class that extends Map and provides array methods + * @abstract Collection + * @extends Map + */ + export default class Collection extends Map { + /** + * Convert Map to array + */ + public toArray() { + return [...this.values()]; + } + + /** + * Filter values by function + */ + public filter(callbackFn: any, thisArg?: any) { + return this.toArray().filter(callbackFn, thisArg); + } + + /** + * Map values by function + */ + public map(callbackFn: any, thisArg?: any) { + return this.toArray().map(callbackFn, thisArg); + } + + /** + * Reduce values by function + */ + public reduce(callbackFn: any, currentIndex: number) { + return this.toArray().reduce(callbackFn, currentIndex); + } + + /** + * Find values by function + */ + public find(predicate: any, thisArg: any) { + return this.toArray().find(predicate, thisArg); + } +} diff --git a/src/Structures/Command.ts b/src/Structures/Command.ts index 62567831..88f41b76 100644 --- a/src/Structures/Command.ts +++ b/src/Structures/Command.ts @@ -6,6 +6,7 @@ interface Command { cooldown?: number; defaultCommand?: string; disableDM?: boolean; + [key: string]: any; } /** @@ -21,10 +22,34 @@ abstract class Command extends Base { public abstract example: string|string[]; public abstract expectedArgs: number; - public abstract execute(e: any): Promise; + public abstract execute(e: CommandMetadata): Promise; + + public ensureInterface() { + // required properties + if (this.aliases == undefined) { + throw new Error(`${this.constructor.name} command must define aliases property.`); + } + if (this.description == undefined) { + throw new Error(`${this.constructor.name} command must define description property.`); + } + if (this.usage == undefined) { + throw new Error(`${this.constructor.name} command must define usage property.`); + } + + // required methods + if (this.execute == undefined) { + throw new Error(`${this.constructor.name} command must have an execute method.`); + } + + // warnings + if (this.expectedArgs == undefined) { + this.logger.warn(`${this.constructor.name} should defined the expectedArgs property.`); + } + this.expectedArgs = this.expectedArgs || 0; + } public help(message: djs.Message): Promise { - const msg = []; + const msg: string[] = []; const command: any = this; const name = this.name; const description = `**Description:**` @@ -54,7 +79,7 @@ abstract class Command extends Base { public _execute(e: CommandMetadata): Promise { const { message, args, command } = e; - if ((this.expectedArgs && args.length < this.expectedArgs) || (args && args[0] == 'help')) { + if ((this.expectedArgs && args!.length < this.expectedArgs) || (args && args[0] == 'help')) { return this.help(message); } diff --git a/src/Structures/EventCollection.ts b/src/Structures/EventCollection.ts new file mode 100644 index 00000000..3f2c2682 --- /dev/null +++ b/src/Structures/EventCollection.ts @@ -0,0 +1,69 @@ +//@ts-nocheck + +import { EventEmitter } from 'events'; +import Collection from './Collection'; + +const collection = Symbol('collection'); + +export default class EventCollection extends EventEmitter { + constructor() { + super(); + this[collection] = new Collection(); + } + + get size() { + return this[collection].size; + } + + get length() { + return this[collection].size; + } + + public has(key: any) { + return this[collection].has(key); + } + + public get(key: any) { + return this[collection].get(key); + } + + public set(key: any, value: any) { + return this[collection].set(key, value); + } + + public delete(key: any) { + return this[collection].delete(key); + } + + public keys() { + return this[collection].keys(); + } + + public values() { + return this[collection].values(); + } + + public entries() { + return this[collection].entries(); + } + + public forEach(...args: any[]) { + return this[collection].forEach(...args); + } + + public filter(...args: any[]) { + return this[collection].filter(...args); + } + + public map(...args: any[]) { + return this[collection].map(...args); + } + + public reduce(...args: any[]) { + return this[collection].reduce(...args); + } + + public find(...args: any[]) { + return this[collection].find(...args); + } +} diff --git a/src/Structures/ICommand.ts b/src/Structures/ICommand.ts new file mode 100644 index 00000000..dc71a7e1 --- /dev/null +++ b/src/Structures/ICommand.ts @@ -0,0 +1,14 @@ +import CommandMetadata from './CommandMetadata'; + +export default interface CommandInterface { + group : string; + module? : string; + aliases : string[]; + description : string; + expectedArgs : number; + cooldown : number; + usage : string|string[]; + defaultUsage? : string; + disableDM? : boolean; + execute(data: CommandMetadata): Promise<{}>; +} diff --git a/src/Structures/Module.ts b/src/Structures/Module.ts index 42727692..2aeb3565 100644 --- a/src/Structures/Module.ts +++ b/src/Structures/Module.ts @@ -1,5 +1,4 @@ import * as djs from 'discord.js'; -import * as naga from '../Core/Naga'; import Base from './Base'; interface Module { @@ -16,6 +15,17 @@ abstract class Module extends Base { public abstract start(client: djs.Client, ...args: any[]): any; + public ensureInterface() { + // required properties + if (this.module == undefined) { + throw new Error(`${this.constructor.name} command must define module property.`); + } + if (this.enabled == undefined) { + throw new Error(`${this.constructor.name} command must define enabled property.`); + } + } + + public _start(client: djs.Client, ...args: any[]): any { if (this.start) { this.start(client, ...args); diff --git a/src/Structures/Utils.ts b/src/Structures/Utils.ts index df60f774..9d6a9f0f 100644 --- a/src/Structures/Utils.ts +++ b/src/Structures/Utils.ts @@ -1,4 +1,7 @@ import * as djs from 'discord.js'; +import path from 'path'; + +const MAX_ITERATIONS_PER_TICK = 500; /** * Utility functions, from the Utils class of @dyno.gg/dyno-core. @@ -29,7 +32,7 @@ export default class Utils { return str.replace(this.cleanRegex, '\\$&'); } - public fullName(user: djs.User): string { + public fullName(user: djs.User|djs.ClientUser): string { user = user; let username = user.username; @@ -112,6 +115,4 @@ export default class Utils { public hexToInt(color: string): number { return color.startsWith('#') ? parseInt(color.replace('#', ''), 16) : parseInt(color, 16); } -} - -export const utils = new Utils(); \ No newline at end of file +}; \ No newline at end of file diff --git a/src/Structures/index.ts b/src/Structures/index.ts new file mode 100644 index 00000000..8a44e120 --- /dev/null +++ b/src/Structures/index.ts @@ -0,0 +1,7 @@ +export { default as Base } from './Base'; +export { default as Collection } from './Collection'; +export { default as Command } from './Command'; +export { default as CommandMetadata } from './CommandMetadata'; +export { default as EventCollection } from './EventCollection'; +export { default as Module } from './Module'; +export { default as Utils } from './Utils'; \ No newline at end of file diff --git a/src/index.d.ts b/src/index.d.ts new file mode 100644 index 00000000..6c805aa3 --- /dev/null +++ b/src/index.d.ts @@ -0,0 +1,130 @@ +import * as djs from 'discord.js'; + +declare module Naga { + export interface NagaConfig { + name: string; + author: string; + version: string|number; + prefix: string; + adminPrefix: string; + developers: string[]; + contributors: object[]; + test: boolean; + pkg: any; + avatarGuild: string; + testGuilds: string[]; + modules: string[]; + client: ClientConfig; + } + + export interface ClientConfig { + id: string; + token: string; + allowedMentions: string[]; + fetchAllUsers: boolean; + intents: djs.IntentsBitField; + messageCacheSize: number; + } + + export interface GlobalConfig { + prefix: string; + commands: {[key: string]: boolean}; + modules: {[key: string]: boolean}; + ignoredUsers: string[]; + [key: string]: any; + } + + export interface GuildConfig { + id: string; + name: string; + owner: string; + prefix: string; + modules: {[key: string]: boolean}; + commands: {[key: string]: boolean}; + adminRoles?: string[]; + modRoles?: string[]; + test?: boolean; + ignoredUsers?: string[]; + ignoredRoles?: string[]; + [key: string]: any; + } + + export class Naga { + public isReady: boolean; + public client: djs.Client; + public readonly config: NagaConfig; + public readonly globalconfig: GlobalConfig; + public readonly utils: Utils; + [key: string]: any; + } + + export class Base { + public readonly client: djs.Client; + public readonly naga: Naga; + public readonly config: NagaConfig; + public readonly globalconfig: GlobalConfig; + public readonly utils: Utils; + constructor(naga: Naga, guild: djs.Guild); + public isDeveloper(user: djs.User): boolean; + public isServerAdmin(member: djs.GuildMember, channel: djs.GuildChannel): boolean; + public isServerMod(member: djs.GuildMember): boolean; + public sendMessage(channel: djs.TextBasedChannel, content: any, options?: any): Promise; + } + + export class Module extends Base { + constructor(naga: Naga); + [key: string]: any; + } + + export interface ICommand { + group : string; + module? : string; + aliases : string[]; + description : string; + expectedArgs : number; + cooldown : number; + usage : string|string[]; + defaultUsage? : string; + disableDM? : boolean; + execute(data: CommandMetadata): Promise<{}>; + } + + export interface CommandMetadata { + message: djs.Message; + args?: any[]; + t?: Function; + command?: string; + guildConfig?: GuildConfig; + isDev?: boolean; + isAdmin?: boolean; + } + + export class Command extends Base { + public name: string; + public aliases: string[]; + constructor(naga: Naga); + public help(message: djs.Message, guildConfig: GuildConfig): Promise; + [key: string]: any; + } + + export class Utils { + public regEscape(str: string): string; + public clean(str: string); + public fullName(user: djs.User): string; + public async sendMessage(channel: djs.TextBasedChannel, message: any, options: any = {}): Promise; + public sendCode(channel: djs.TextBasedChannel, message: string = ' ', lang: string = '', options: any = {}): Promise; + public sortRoles(roles: any): djs.Role[]; + public hexToInt(color: string): number; + } + + export class PermissionsManager { + constructor(naga: Naga); + public naga: Naga; + public isDeveloper(user: djs.User): boolean; + public isServerAdmin(member: djs.GuildMember, channel: djs.GuildChannel): boolean; + public isServerMod(member: djs.GuildMember): boolean; + + } +} + +export = Naga; \ No newline at end of file From 6b2643b263b57c6ef38eb0319c6b91a0d94893e2 Mon Sep 17 00:00:00 2001 From: Kyle S Date: Wed, 20 Mar 2024 19:30:33 -0400 Subject: [PATCH 3/6] Fixed utils --- src/Structures/Utils.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/Structures/Utils.ts b/src/Structures/Utils.ts index 9d6a9f0f..26d2f36a 100644 --- a/src/Structures/Utils.ts +++ b/src/Structures/Utils.ts @@ -1,23 +1,12 @@ import * as djs from 'discord.js'; -import path from 'path'; - -const MAX_ITERATIONS_PER_TICK = 500; /** - * Utility functions, from the Utils class of @dyno.gg/dyno-core. - * @license Creative-Commons-Attribution-NonCommercial-NoDerivs-4.0 - * @author Dyno Developers + * Utility functions, based on @dyno.gg/dyno-core */ export default class Utils { - private channelRegex: RegExp = new RegExp('{#([a-zA-Z0-9-_]+)}', 'g'); - private roleRegex: RegExp = new RegExp('{&([^}]+)}', 'g'); - private userRegex: RegExp = new RegExp('{@([^}]+)}', 'g'); - private emojiRegex: RegExp = new RegExp(/<(a)?:(\w+):(\d+)>/, 'g'); private cleanRegex: RegExp = new RegExp('([_\*`])', 'g'); - private lastMessage: number; - - constructor() {} + private lastMessage: number; /** * Escapes special characters from regex @@ -39,11 +28,9 @@ export default class Utils { let discrim = user.discriminator; if (!username) return user.id; - username = this.clean(username); if (discrim === '0') return username; - return `${username}#${discrim}`; } From b13b7cf9cf636db563fd5dace2ca6285234205fb Mon Sep 17 00:00:00 2001 From: Kyle S Date: Thu, 21 Mar 2024 00:50:46 -0400 Subject: [PATCH 4/6] Config stuff --- src/Core/Config.ts | 52 ++++++++++++++++++++++++++++++++++ src/Managers/CommandManager.ts | 2 ++ src/index.d.ts | 26 +++++++++++------ src/start.ts | 11 +++---- 4 files changed, 76 insertions(+), 15 deletions(-) create mode 100644 src/Core/Config.ts diff --git a/src/Core/Config.ts b/src/Core/Config.ts new file mode 100644 index 00000000..663a2a83 --- /dev/null +++ b/src/Core/Config.ts @@ -0,0 +1,52 @@ +import Naga from '../../'; +import * as djs from 'discord.js'; + +const pkg = require('../../package.json'); + +let config: Naga.NagaConfig = { + name: pkg.name, + version: pkg.version, + author: 'Kyle S', + prefix: 'n.', + adminPrefix: '$', + state: 1341, + stateName: 'Development', + client: { + id: '966189242969636866', + token: process.env.TOKEN, + options: { + allowedMentions: { parse: [ 'roles'] }, + fetchAllUsers: true, + intents: process.env.INTENTS as unknown as djs.IntentsBitField, + messageCacheSize: 5000, + } + }, + database: { + uri: process.env.DB_CONNECTION, + }, + emojis: { + success: '', + error: '', + info: '' + }, + status: [ + { name: 'Testing', type: 0 }, + { name: 'Naga v3', type: 0 }, + { name: 'n.help', type: 0 } + ], + avatarGuild: '370708369951948800', + testGuilds: [ + '546800805060280352', // Quantum Biotics, Inc. (soda) + '504742982604554241' // TwoDog Fan Club + ], + developers: [ + '254814547326533632', // Soda + '123261299864895489' // TwoDog + ], + contributors: [ + '260600155630338048' // Avatar Bolin + ], + pkg: pkg, +}; + +export default config; \ No newline at end of file diff --git a/src/Managers/CommandManager.ts b/src/Managers/CommandManager.ts index 9609512a..6066d666 100644 --- a/src/Managers/CommandManager.ts +++ b/src/Managers/CommandManager.ts @@ -31,5 +31,7 @@ export default class CommandManager extends EventCollection { command.name = command.aliases[0]; this.logger.info(`Registering command ${command.name}`); + + this.naga.models.Command.update({ name: command.name, _state: this.naga.config.state }, command.toJSON(), { upsert: true }) } } \ No newline at end of file diff --git a/src/index.d.ts b/src/index.d.ts index 6c805aa3..50dbb18d 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -5,25 +5,36 @@ declare module Naga { name: string; author: string; version: string|number; + state: number; + stateName: string; prefix: string; adminPrefix: string; developers: string[]; - contributors: object[]; - test: boolean; + contributors: string[]; pkg: any; avatarGuild: string; testGuilds: string[]; - modules: string[]; client: ClientConfig; + database: { + uri: string; + }; + emojis: { + success: string; + error: string; + info: string; + }; + status: object; } export interface ClientConfig { id: string; token: string; - allowedMentions: string[]; - fetchAllUsers: boolean; - intents: djs.IntentsBitField; - messageCacheSize: number; + options: { + allowedMentions: djs.MessageMentionOptions; + fetchAllUsers: boolean; + intents: djs.IntentsBitField; + messageCacheSize: number; + } } export interface GlobalConfig { @@ -123,7 +134,6 @@ declare module Naga { public isDeveloper(user: djs.User): boolean; public isServerAdmin(member: djs.GuildMember, channel: djs.GuildChannel): boolean; public isServerMod(member: djs.GuildMember): boolean; - } } diff --git a/src/start.ts b/src/start.ts index da77bdfd..b66f3872 100644 --- a/src/start.ts +++ b/src/start.ts @@ -1,11 +1,8 @@ import Naga from './Core/Naga'; -const config: any = { - client: { - intents: process.env.INTENTS - } -} - const naga = new Naga(); -naga.setup(config.client); \ No newline at end of file +console.info('NAGA - Version: 3.0.0, Build: 17\n\n'); + +console.info('Initializing client'); +naga.setup(naga.config.client.options); \ No newline at end of file From 8ceeff36d881c6e7aa4baaea13ce7988890faa05 Mon Sep 17 00:00:00 2001 From: Kyle S Date: Thu, 21 Mar 2024 01:01:05 -0400 Subject: [PATCH 5/6] More config stuff --- package-lock.json | 318 +++++++++++++++++++++++++++++---------------- package.json | 7 +- src/Core/Config.ts | 2 +- src/Core/Naga.ts | 17 +++ 4 files changed, 226 insertions(+), 118 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8a505724..ee7e6e8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,8 @@ "dependencies": { "async-each": "^1.0.6", "discord.js": "^14.14.1", - "glob-promise": "^6.0.5", - "minimatch": "^9.0.3" - }, - "devDependencies": { - "@types/node": "^20.11.25" + "mongoose": "^8.2.2", + "mongoose-long": "^0.8.0" } }, "node_modules/@discordjs/builders": { @@ -124,6 +121,14 @@ "node": ">=14" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz", + "integrity": "sha512-XLNOMH66KhJzUJNwT/qlMnS4WsNDWD5ASdyaSH3EtK+F4r/CFGa3jT4GNi4mfOitGvWXtdLgQJkQjxSVrio+jA==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@sapphire/async-queue": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.2.tgz", @@ -154,20 +159,6 @@ "npm": ">=7.0.0" } }, - "node_modules/@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", - "dependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" - }, "node_modules/@types/node": { "version": "20.11.25", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz", @@ -176,6 +167,19 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", + "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/ws": { "version": "8.5.9", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", @@ -204,17 +208,28 @@ } ] }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "node_modules/bson": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.5.0.tgz", + "integrity": "sha512-DXf1BTAS8vKyR90BO4x5v3rKVarmkdkzwOrnYDFdjAY694ILNDkmA3uRh1xXJEl+C1DAh8XCvAQ+Gh3kzubtpg==", + "engines": { + "node": ">=16.20.1" + } }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "balanced-match": "^1.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/discord-api-types": { @@ -251,113 +266,176 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "peer": true - }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "peer": true, + "node_modules/kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, + "node_modules/magic-bytes.js": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", + "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==" + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, + "node_modules/mongodb": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", + "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^3.0.0" }, "engines": { - "node": ">=12" + "node": ">=16.20.1" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", + "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" } }, - "node_modules/glob-promise": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-6.0.5.tgz", - "integrity": "sha512-uUzvxo60yo/vMLXZHCNAlfdM5U5A07jCnUO8xTK44Z0Vc58poGDXhDx8ju1DmPdprOORh+4Lpog64hl+AJ5piA==", + "node_modules/mongoose": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.2.tgz", + "integrity": "sha512-6sMxe1d3k/dBjiOX4ExNTNOP0g1x0iq8eXyg+ttgIXM3HLnQ0IUyXRwVVAPFFY6O4/8uYN5dB0Ec72FrexbPpw==", "dependencies": { - "@types/glob": "^8.0.0" + "bson": "^6.2.0", + "kareem": "2.5.1", + "mongodb": "6.3.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "16.0.1" }, "engines": { - "node": ">=16" + "node": ">=16.20.1" }, "funding": { - "type": "individual", - "url": "https://github.com/sponsors/ahmadnassri" - }, + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose-long": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/mongoose-long/-/mongoose-long-0.8.0.tgz", + "integrity": "sha512-gNpLDs9HE5W5rKuZPFOzFB1Ei/SJbSSA6HlF/wR5qGU3N9K5SXfXlvWm+uUWpqZ13H3Zhna3Ww6ILGW4TjFP+g==", "peerDependencies": { - "glob": "^8.0.3" + "mongoose": "4.x || 5.x || 6.x || 7.x || 8.x" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "peer": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", "engines": { - "node": ">=10" + "node": ">=4.0.0" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "peer": true, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "peer": true + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } }, - "node_modules/magic-bytes.js": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", - "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==" + "node_modules/sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "memory-pager": "^1.0.2" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "peer": true, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", "dependencies": { - "wrappy": "1" + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" } }, "node_modules/ts-mixer": { @@ -386,11 +464,25 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "peer": true + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } }, "node_modules/ws": { "version": "8.14.2", diff --git a/package.json b/package.json index ffd416b3..23c1f887 100644 --- a/package.json +++ b/package.json @@ -18,14 +18,13 @@ }, "keywords": [ "Discord", - "Discord", - "Bot" + "Discord Bot" ], "dependencies": { "async-each": "^1.0.6", "discord.js": "^14.14.1", - "glob-promise": "^6.0.5", - "minimatch": "^9.0.3" + "mongoose": "^8.2.2", + "mongoose-long": "^0.8.0" }, "license": "ISC" } diff --git a/src/Core/Config.ts b/src/Core/Config.ts index 663a2a83..6885ad97 100644 --- a/src/Core/Config.ts +++ b/src/Core/Config.ts @@ -4,7 +4,7 @@ import * as djs from 'discord.js'; const pkg = require('../../package.json'); let config: Naga.NagaConfig = { - name: pkg.name, + name: pkg.name.charAt(0).toUpperCase() + pkg.name.slice(1), version: pkg.version, author: 'Kyle S', prefix: 'n.', diff --git a/src/Core/Naga.ts b/src/Core/Naga.ts index 7b510762..82d7960f 100644 --- a/src/Core/Naga.ts +++ b/src/Core/Naga.ts @@ -1,5 +1,6 @@ import * as djs from 'discord.js'; +import config from './Config'; import Logger from './Logger'; import { Utils } from '../Structures'; @@ -7,6 +8,8 @@ import PermissionsManager from '../Managers/PermissionsManager'; import CommandManager from '../Managers/CommandManager'; import ModuleManager from '../Managers/ModuleManager'; +import SpiritLibrary from './Database/Database'; + export default class Naga { private _client: djs.Client; public isReady: boolean; @@ -16,6 +19,7 @@ export default class Naga { public permissions: PermissionsManager; public commands: CommandManager; public modules: ModuleManager; + public _db: SpiritLibrary; constructor() { this.isReady = false; @@ -25,6 +29,10 @@ export default class Naga { return this._client; } + public get config() { + return config; + } + public get utils() { return this._utils; } @@ -33,6 +41,14 @@ export default class Naga { return this._logger; } + public get db() { + return this._db; + } + + public get models() { + return this.db.models; + } + /** * Initializes the bot client * @param opts Client options @@ -43,6 +59,7 @@ export default class Naga { options.allowedMentions = opts.allowedMentions || { parse: [ 'roles'] }; options.intents = opts.intents || [djs.GatewayIntentBits.Guilds]; + this._db = new SpiritLibrary(this.config.database.uri); this._logger = new Logger(); this._utils = new Utils(); From 3be99725961a92065ee873c1d32c93a91d2b5f9c Mon Sep 17 00:00:00 2001 From: Kyle S Date: Thu, 21 Mar 2024 21:20:06 -0400 Subject: [PATCH 6/6] Add GuildManager, progress on other handlers --- src/Core/Config.ts | 3 +- src/Core/Naga.ts | 107 ++++++++++++++- src/Managers/CommandManager.ts | 2 +- src/Managers/GuildManager.ts | 234 +++++++++++++++++++++++++++++++++ src/Managers/ModuleManager.ts | 3 +- src/index.d.ts | 1 + src/logo.ts | 8 ++ src/start.ts | 46 ++++++- 8 files changed, 392 insertions(+), 12 deletions(-) create mode 100644 src/Managers/GuildManager.ts create mode 100644 src/logo.ts diff --git a/src/Core/Config.ts b/src/Core/Config.ts index 6885ad97..0fe1e6b0 100644 --- a/src/Core/Config.ts +++ b/src/Core/Config.ts @@ -3,7 +3,7 @@ import * as djs from 'discord.js'; const pkg = require('../../package.json'); -let config: Naga.NagaConfig = { +let config: any = { name: pkg.name.charAt(0).toUpperCase() + pkg.name.slice(1), version: pkg.version, author: 'Kyle S', @@ -15,6 +15,7 @@ let config: Naga.NagaConfig = { id: '966189242969636866', token: process.env.TOKEN, options: { + maxShards: 1, allowedMentions: { parse: [ 'roles'] }, fetchAllUsers: true, intents: process.env.INTENTS as unknown as djs.IntentsBitField, diff --git a/src/Core/Naga.ts b/src/Core/Naga.ts index 82d7960f..98c58e28 100644 --- a/src/Core/Naga.ts +++ b/src/Core/Naga.ts @@ -1,50 +1,68 @@ import * as djs from 'discord.js'; - +import { GlobalConfig } from '../'; import config from './Config'; import Logger from './Logger'; import { Utils } from '../Structures'; import PermissionsManager from '../Managers/PermissionsManager'; import CommandManager from '../Managers/CommandManager'; +import GuildManager from '../Managers/GuildManager'; import ModuleManager from '../Managers/ModuleManager'; import SpiritLibrary from './Database/Database'; +var name = 'BotClient'; +var nagaConfig = config; + export default class Naga { private _client: djs.Client; public isReady: boolean; - + public _globalConfig: GlobalConfig; public _utils: Utils; public _logger: Logger; public permissions: PermissionsManager; public commands: CommandManager; + public guilds: GuildManager; public modules: ModuleManager; public _db: SpiritLibrary; + public _globalConfigInterval: NodeJS.Timeout; + public isLoading: any; constructor() { this.isReady = false; } + // discord.js client public get client() { return this._client; } + // bot config public get config() { return config; } + // global config + public get globalConfig() { + return this._globalConfig; + } + + // common utility functions public get utils() { return this._utils; } + // logger instance public get logger() { return this._logger; } + // mongo instance public get db() { return this._db; } + // mongo models public get models() { return this.db.models; } @@ -64,29 +82,78 @@ export default class Naga { this._utils = new Utils(); // Initialize the discord.js client + this.logger.info('Initializing client', name); this._client = new djs.Client(options); // Initialize manager classes this.permissions = new PermissionsManager(this); this.commands = new CommandManager(this); this.modules = new ModuleManager(this); + this.guilds = new GuildManager(this); // Event functions this.client.once('ready', this.ready.bind(this)); - this.client.on('error', err => this.logger.error(err)) + this.client.on('error', err => this.logger.error(err)); + + this.client.guilds.cache.forEach(g => { + this.loadGuild(g.id); + }) + + await this.loadConfig().catch(() => null); + await this.watchGlobalConfig(); // Connect to Discord const token = process.env.TOKEN as string; await this.login(token); } + /** + * Loads config from the db + */ + public async loadConfig(): Promise { + try { + this.logger.info('Loading config', name); + if (this.models.Config != undefined) { + const dbConfig = await this.models.Config.findOne({ clientId: this.config.client.id }).lean() + if (dbConfig) { + nagaConfig = Object.assign(config, dbConfig); + } + this.logger.info('Loading globalConfig', name) + const globalConfig: GlobalConfig = await this.models.Naga.findOne().lean(); + this._globalConfig = config.global = globalConfig; + } + } catch (err) { + this.logger.error(`Unable to load config: ${err}`, name); + } + } + + async watchGlobalConfig() { + await this.updateGlobalConfig(); + this._globalConfigInterval = setInterval(() => this.updateGlobalConfig(), 2 * 60 * 1000); + } + + /** + * Updates the global config db doc + */ + public async updateGlobalConfig(): Promise { + try { + this.logger.info('Updating globalConfig') + const globalConfig: GlobalConfig = await this.models.Naga.findOne().lean(); + if (globalConfig) { + this._globalConfig = config.global = globalConfig; + } + } catch (err) { + this.logger.error(`Unable to update globalConfig: ${err}`, name); + } + } + /** * Connects to the Discord gateway * @param t Bot token */ public async login(t: string): Promise { const token = 'Bot ' + t; - this.logger.info('Connecting to Discord...', 'BotClient'); + this.logger.info('Connecting to Discord', name); await this.client.login(token); } @@ -103,9 +170,39 @@ export default class Naga { * Executes when the bot fires the "ready" event */ public async ready(): Promise { - this.logger.info(`Successfully connected to Discord as ${this.utils.fullName(this.client.user)}`, 'BotClient'); + this.logger.info(`Connected to Discord as ${this.utils.fullName(this.client.user)}`, name); this.isReady = true; await this.editStatus({name: 'Testing', type: 0}) } + + public loadGuild(id: string) { + + let g = this.client.guilds.cache.get(id); + + this.guilds.getOrFetch(g.id); + const p = new Promise(async (res, rej) => { + try { + let [guild, channels] = await Promise.all([ + this.client.guilds.cache.get(id), + this.client.guilds.cache.get(id).channels, + ]); + + guild.channels = channels; + + return res(guild); + } catch (err) { + return rej(err); + } + }); + + this.isLoading.set(id, p); + + setTimeout(() => { + if (!this.isLoading.has(id)) { return; } + this.isLoading.delete(id); + }, 10000); + + return p; + }; } \ No newline at end of file diff --git a/src/Managers/CommandManager.ts b/src/Managers/CommandManager.ts index 6066d666..b21a8ceb 100644 --- a/src/Managers/CommandManager.ts +++ b/src/Managers/CommandManager.ts @@ -32,6 +32,6 @@ export default class CommandManager extends EventCollection { this.logger.info(`Registering command ${command.name}`); - this.naga.models.Command.update({ name: command.name, _state: this.naga.config.state }, command.toJSON(), { upsert: true }) + this.naga.models.Command.updateMany({ name: command.name, _state: this.naga.config.state }, command.toJSON(), { upsert: true }) } } \ No newline at end of file diff --git a/src/Managers/GuildManager.ts b/src/Managers/GuildManager.ts new file mode 100644 index 00000000..fd437e1c --- /dev/null +++ b/src/Managers/GuildManager.ts @@ -0,0 +1,234 @@ +import { Client, Guild } from 'discord.js'; +import { Collection } from '../Structures'; +import Naga from '../'; + +export default class GuildManager extends Collection { + public naga: Naga.Naga; + public client: Client; + public config: Naga.NagaConfig; + public _activeThreshold: number; + public _registering: any; + + constructor(naga: any) { + super(); + this.naga = naga; + this.client = naga.client; + this.config = naga.config; + this._registering = new Set(); + this._activeThreshold = 3600 * 1000; // 24 hrs + } + + // Global config + get globalConfig() { + return this.naga.globalConfig; + } + + /** + * Update a guild's database entry + * @param id Guild ID + * @returns + */ + public guildUpdate(id: string) { + if (!this.client.guilds.cache.has(id)) return; + } + + public getOrFetch(id: string) { + const doc = this.get(id); + if (doc) { + doc.cachedAt = Date.now(); + return Promise.resolve(doc); + } + + return this.fetch(id).then((doc: any) => { + if (!doc) { + return this.registerGuild(this.client.guilds.cache.get(id)); + } + + doc.cachedAt = Date.now(); + this.set(doc._id, doc); + + return doc; + }); + } + + /** + * Fetches a guild from the database + * @param id Guild ID + * @returns + */ + public fetch(id: string) { + let updateKeys = ['name', 'iconURL', 'ownerID', 'memberCount']; + return new Promise((resolve, reject) => { + this.naga.models.Server.findAndPopulate(id).then((doc: any) => { + if (!doc) return resolve(); + doc = doc.toObject(); + let update: any = false; + + if (this.client.guilds.cache.has(id)) { + const guild = this.naga.guilds.cache.get(id); + + if (!doc.longId) { + update = update || {}; + update.longId = guild.id; + }; + + for (let key of updateKeys) { + if (guild[key] && doc[key] !== guild[key]) { + update = update || {}; + update[key] = guild[key]; + doc[key] = guild[key]; + } + } + + if (doc.deleted === true) { + update = update || {}; + update.deleted = false; + } + + if (!doc.lastActive || (Date.now() - doc.lastActive) > this._activeThreshold) { + update = update || {}; + update.lastActive = Date.now(); + this.setActive(guild, update.lastActive); + } + + if (update) { + this.update(id, { $set: update }).catch((err: Error) => this.naga.logger.error(err)); + } + } + this.set(doc._id, doc); + return resolve(doc); + }).catch((err: Error) => reject(err)); + }); + } + + setActive(guild: any, time: Date) { + guild.lastActive = time; + this.naga.redis.hset(`guild_activity:${this.config.client.id}:${this.config.client.options.maxShards}:${guild.shard.id}`, guild.id, time) + .catch((): any => null); + } + + /** + * Guild created event listener + * @param {Guild} guild Guild object + */ + async guildCreated(guild: Guild) { + this.naga.logger.info(`Connected to guild: ${guild.id} with ${guild.channels.cache.size} channels and ${guild.members.cache.size} members | ${guild.name}`); + + try { + var doc = await this.naga.models.Server.findOne({ _id: guild.id }).lean().exec(); + if (!doc) { + return this.registerGuild(guild); + } + + await this.update(guild.id, { $set: { deleted: false } }, { multi: true }); + this.set(doc._id, doc); + } catch (err) { + return this.naga.logger.error(err); + } + return false; + } + + /** + * Guild deleted event listener + * @param {Guild} guild Guild object + */ + async guildDeleted(guild: Guild) { + if (!guild.available) return; + + this.update(guild.id, { $set: { deleted: true, deletedAt: new Date() } }) + .catch((err: Error) => this.naga.logger.error(err)); + } + + /** + * Loads a guild into the database + * @param guild Guild object + * @returns + */ + public registerGuild(guild: Guild) { + console.log('hi') + if (!guild) return; + + let doc: any = { + _id: guild.id, + clientID: this.config.client.id, + name: guild.name, + iconURL: guild.iconURL, + ownerID: guild.ownerId, + memberCount: guild.memberCount, + modules: {}, + commands: {}, + lastActive: Date.now() + }; + + this.naga.logger.info(`Registering guild ${guild.name} (${guild.id})`); + + return new Promise((resolve, reject) => { + for (let module of this.naga.modules.values()) { + if (module.core && (module.hasOwnProperty('list') && module.list === false)) continue; + doc.modules[module.module] = module.enabled; + } + + for (let command of this.naga.commands.values()) { + if (command.permissions === 'admin') continue; + + if (this.naga.modules.find((c: Naga.Command) => c.module === command.group) && doc.modules[command.group] === false) { + doc.commands[command.name] = false; + continue; + } + + doc.commands[command.name] = (command.enabled || !command.disabled); + } + + // update method implementation + }) + } + + /** + * Update a guild + * @param id Guild ID + * @param update + * @param args Args + * @returns + */ + public update(id: string, update: any, ...args: any[]) { + if (update.$set) { + const serverlistColl = this.naga.db.collection('serverlist_store'); + let serverlistUpdate: any = false; + if (update.$set.iconURL) { + serverlistUpdate = serverlistUpdate || {}; + serverlistUpdate.iconURL = update.$set.iconURL; + } + + if (update.$set.deleted === true) { + serverlistUpdate = serverlistUpdate || {}; + serverlistUpdate.markedForDeletionAt = Date.now(); + } + + if (update.$set.name) { + serverlistUpdate = serverlistUpdate || {}; + serverlistUpdate.name = update.$set.name; + } + + if (update.$set.memberCount) { + serverlistUpdate = serverlistUpdate || {}; + serverlistUpdate.memberCount = update.$set.memberCount; + } + + if (serverlistUpdate) { + serverlistColl.update({ id }, { $set: serverlistUpdate }); + } + + if (update.$set.deleted === false) { + serverlistColl.update({ id }, { $unset: { markedForDeletionAt: 1 } }); + } + } + + try { + const result = this.naga.models.Server.update({ _id: id }, update, ...args); + this.naga.redis.publish('guildConfig', id); + return result; + } catch (err) { + this.naga.logger.error(err); + } + } +} \ No newline at end of file diff --git a/src/Managers/ModuleManager.ts b/src/Managers/ModuleManager.ts index c438c3d8..bcece47b 100644 --- a/src/Managers/ModuleManager.ts +++ b/src/Managers/ModuleManager.ts @@ -30,10 +30,9 @@ export default class ModuleManager extends Collection { try { this.register(module); } catch (e) { - this.logger.error(__filename, e); + this.logger.error(e); } } - this.logger.info(`Registered ${Object.keys(modules).length} modules.`) } diff --git a/src/index.d.ts b/src/index.d.ts index 50dbb18d..1bfd3daf 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -30,6 +30,7 @@ declare module Naga { id: string; token: string; options: { + maxShards: number; allowedMentions: djs.MessageMentionOptions; fetchAllUsers: boolean; intents: djs.IntentsBitField; diff --git a/src/logo.ts b/src/logo.ts new file mode 100644 index 00000000..7dae02ae --- /dev/null +++ b/src/logo.ts @@ -0,0 +1,8 @@ +export = [ +' _ ____ _____ ____ ', +' / \\ /|/ _ \\/ __// _ \\ ', +' | |\\ ||| / \\|| | _| / \\| ', +' | | \\||| |-||| |_//| |-|| ', +' \\_/ \\|\\_/ \\|\\____/\\_/ \\| ', +' ', +]; \ No newline at end of file diff --git a/src/start.ts b/src/start.ts index b66f3872..5b7c510e 100644 --- a/src/start.ts +++ b/src/start.ts @@ -1,8 +1,48 @@ import Naga from './Core/Naga'; +import { readFileSync } from 'fs'; +import util from 'util'; +import { exec } from 'child_process'; +import config from './Core/Config'; const naga = new Naga(); -console.info('NAGA - Version: 3.0.0, Build: 17\n\n'); +let logo = require('./logo'); +logo = logo.join('\n'); -console.info('Initializing client'); -naga.setup(naga.config.client.options); \ No newline at end of file +function log(...args: any[]) { + process.stdout.write(`${util.format.apply(null, args)}\n`); +} + +async function init() { + log(logo); + log(`${config.name} ${config.stateName} ${config.pkg.version}`, '\n'); + + if (config.stateName === 'Development') { + try { + log(`Recent Git commits:`); + await gitInfo(); + } catch (err) { + console.error(err); + } + } + naga.setup(naga.config.client.options); +} + +function gitInfo(): Promise { + return new Promise((res, rej) => + exec('git log -n 3 --no-color --pretty=format:\'[ "%h", "%s", "%cr", "%an" ],\'', (err, stdout) => { + if (err) { + return rej(err); + } + + let str = stdout.split('\n').join(''); + str = str.substr(0, str.length - 1); + + let lines = JSON.parse(`[${str}]`); + lines = lines.map((l: any) => `[${l[0]}] ${l[1]} - ${l[2]}`); + log(`${lines.join('\n')}\n`); + return res(); + })); +} + +init();