diff --git a/BotNeck.plugin.js b/BotNeck.plugin.js index 3b52e91..dd3a1e5 100644 --- a/BotNeck.plugin.js +++ b/BotNeck.plugin.js @@ -6,6 +6,7 @@ var BotNeckModules = {}; var BotNeckData = { bdPath: "", botneckModules: "", + botneckConfig: "", eWindow: null, Backup_Send: null, }; @@ -16,13 +17,15 @@ var BotNeckConfig = { const { remote, BrowserWindow } = require('electron'); const fs = require("fs"); +BotNeckPlugin.config = "BotNeck"; + BotNeckPlugin.prototype.onMessage = function() {}; BotNeckPlugin.prototype.onSwitch = function() {}; BotNeckPlugin.prototype.observer = function(e) {}; BotNeckPlugin.prototype.getSettingsPanel = function() { - return ""; + return ""; }; BotNeckPlugin.prototype.getName = function() { @@ -118,16 +121,11 @@ BotNeckPlugin.prototype.start = function() type: "rich", description: "Error executing command " + command + "!", color: 0xff6e00, - fields: [ - { - name: "Error", - value: e, - }, - ], } delete msg["content"]; msg["embed"] = emb; + BotNeckAPI.LogError("Error executing command " + command, e); } send.call(this, JSON.stringify(msg)); return; @@ -146,15 +144,18 @@ BotNeckPlugin.prototype.load = function() { BotNeckData.bdPath = (process.platform == 'win32' ? process.env.APPDATA : process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences' : '/var/local') + '/BetterDiscord/'; BotNeckData.botneckModules = BotNeckData.bdPath + "plugins/BotNeck_Modules" + BotNeckData.botneckConfig = BotNeckData.bdPath + "plugins/" + BotNeckPlugin.config + ".json"; BotNeckData.eWindow = remote.getCurrentWindow(); if(!fs.existsSync(BotNeckData.botneckModules)) fs.mkdirSync(BotNeckData.botneckModules); BotNeckAPI.LoadModules(); + BotNeckAPI.LoadConfig(); }; BotNeckPlugin.prototype.unload = function() { + BotNeckAPI.SaveConfig(); BotNeckAPI.UnloadModules(); }; // ------------------------------------------------------ API ------------------------------------------------------------------ // @@ -194,6 +195,45 @@ BotNeckAPI.UnloadModules = function() { BotNeckModules = {}; }; +BotNeckAPI.LoadConfig = function() +{ + if(!fs.existsSync(BotNeckData.botneckConfig)) + return; + + let conf = fs.readFileSync(BotNeckData.botneckConfig, "utf-8"); + let cJson = JSON.parse(conf); + + for(let key in cJson) + { + if(!BotNeckModules.hasOwnProperty(key) && key != "BotNeck") + continue; + if(key != "BotNeck") + if(typeof(BotNeckModules[key].settings) == "undefined") + continue; + + for(let c in cJson[key]) + { + if(key == "BotNeck") + BotNeckConfig[c] = cJson[key][c]; + else + BotNeckModules[key].settings[c] = cJson[key][c]; + } + } + BotNeckAPI.Log("Configuration loaded!"); +} +BotNeckAPI.SaveConfig = function() +{ + if(fs.existsSync(BotNeckData.botneckConfig)) + fs.unlinkSync(BotNeckData.botneckConfig); + + let cfg = {}; + + cfg["BotNeck"] = BotNeckConfig; + for(let mod in BotNeckModules) + if(typeof(BotNeckModules[mod].settings) != "undefined") + cfg[mod] = BotNeckModules[mod].settings; + fs.writeFileSync(BotNeckData.botneckConfig, JSON.stringify(cfg)) +} BotNeckAPI.GetCurrentServerID = function() { @@ -211,3 +251,36 @@ BotNeckAPI.GetDiscordToken = function() // WARNING: This uses an exploit in the document.body.removeChild(elem); return token; }; + +BotNeckAPI.GetParameterValueFromText = function(text, parameterKey) +{ + let i = text.indexOf(parameterKey + "="); + if(i === -1) + return null; + + let txt = text.substring(i + (parameterKey + "=").length); + let value = ""; + + if(txt.startsWith('"')) // Safe to assume it is a string + { + let ind = 1; // 1 to skip the " + + while(ind < txt.length && txt[ind] != '"') // Loop until we hit the end of the text or until we hit a " + { + value += txt[ind]; + ind++; + } + } + else // Not a string + { + let ind = 0; // Start from 0 + + while(ind < txt.length && txt[ind] != ' ') // Loop until we hit the end of the text or until we hit a space + { + value += txt[ind]; + ind++; + } + } + + return value; +}; diff --git a/BotNeck_Modules/backupChat.module.js b/BotNeck_Modules/backupChat.module.js new file mode 100644 index 0000000..cce9a33 --- /dev/null +++ b/BotNeck_Modules/backupChat.module.js @@ -0,0 +1,54 @@ +var backupChat = function() {}; + +backupChat.prototype.command = "backup"; +backupChat.prototype.minArgs = 2; +backupChat.prototype.help = "Backs up the specified amount of chat into a file. Usage: backup "; + +backupChat.prototype.execute = function(msg, args) +{ + $.ajax({ + type: "GET", + url: "https://discordapp.com/api/v6/channels/" + BotNeckAPI.GetCurrentChannelID() + "/messages?limit=" + args[0], + dataType: "json", + contentType: "application/json", + beforeSend: function (xhr) + { + xhr.setRequestHeader("Authorization", BotNeckAPI.GetDiscordToken()); + }, + success: function(data) + { + let chatBackup = BotNeckData.bdPath + "Chat_Backup"; + + if(!fs.existsSync(chatBackup)) + fs.mkdirSync(chatBackup); + + console.log(data); + let chat = data; // Too lazy to replace + let save = ""; + for(let i in chat) + { + if(!chat[i].hasOwnProperty("content") || chat[i].content == "") + continue; + let msg = chat[i].author.username + "#" + chat[i].author.discriminator + ": " + chat[i].content; + + save += msg; + save += "\n"; + } + + if(fs.existsSync(chatBackup + "/" + args[1] + ".log")) + fs.unlinkSync(chatBackup + "/" + args[1] + ".log"); + fs.writeFileSync(chatBackup + "/" + args[1] + ".log", save); + }, + async: false, + }); + + let emb = { + title: "Chat Backup", + type: "rich", + description: "Chat backed up successfully!", + color: 0x0061ff, + }; + + delete msg["content"]; + msg["embed"] = emb; +} diff --git a/BotNeck_Modules/embed.module.js b/BotNeck_Modules/embed.module.js new file mode 100644 index 0000000..404e44f --- /dev/null +++ b/BotNeck_Modules/embed.module.js @@ -0,0 +1,167 @@ +var embed = function() {}; + +embed.prototype.command = "embed"; +embed.prototype.help = "Sends an embeded message based on paramaters. Usage: embed title='whatever' description='whatever'(Spaces are required between paramaters)"; +embed.prototype.settings = { + title: "BotNeck Embed", + type: "rich", + description: "A simple BotNeck Embed", + color: 0x0061ff, + url: "https://github.com/AtiLion/BotNeck-Bot", + timestamp: "now", + footer: { + text: "Simple footer in embed", + icon_url: "", + }, + image: { + url: "", + width: 0, + height: 0, + }, + thumbnail: { + url: "", + width: 0, + height: 0, + }, + video: { + url: "", + width: 0, + height: 0, + }, + provider: { + name: "", + url: "", + }, + author: { + name: "AtiLion", + url: "https://github.com/AtiLion", + icon_url: "https://avatars3.githubusercontent.com/u/20825809?s=460&v=4", + }, +}; + +embed.prototype.execute = function(msg, args) // Warning: Extremely eye killing code. Do not read if sensitive to improper code formatting! +{ + let embd = $.extend({}, this.settings) // Clone array + let text = args.join(" "); + let emb = {}; + + let name = ""; + let value = ""; + let onValue = false; + let isText = false; + for(let i in text) + { + if(!onValue) + { + if(text[i] == "=") + onValue = true; + else + name += text[i]; + } + else + { + if(text[i] == '"') + isText = !isText; + else if(text[i] == " " && !isText) + { + onValue = false; + isText = false; + + emb[name] = (isNaN(value) ? value : Number(value)); + name = ""; + value = ""; + } + else + { + value += text[i]; + } + } + } + emb[name] = (isNaN(value) ? value : Number(value)); + + if(emb.hasOwnProperty("title")) + embd.title = emb.title; + if(emb.hasOwnProperty("type")) + embd.type = emb.type; + if(emb.hasOwnProperty("description")) + embd.description = emb.description; + if(emb.hasOwnProperty("color")) + embd.color = emb.color; + if(emb.hasOwnProperty("url")) + embd.url = emb.url; + if(emb.hasOwnProperty("timestamp")) + embd.timestamp = emb.timestamp; + if(emb.hasOwnProperty("footer_text")) + embd.footer.text = emb.footer_text; + if(emb.hasOwnProperty("footer_icon_url")) + embd.footer.icon_url = emb.footer_icon_url; + if(emb.hasOwnProperty("image_url")) + embd.image.url = emb.image_url; + if(emb.hasOwnProperty("image_width")) + embd.image.width = emb.image_width; + if(emb.hasOwnProperty("image_height")) + embd.image.height = emb.image_height; + if(emb.hasOwnProperty("thumbnail_url")) + embd.thumbnail.url = emb.thumbnail_url; + if(emb.hasOwnProperty("thumbnail_width")) + embd.thumbnail.width = emb.thumbnail_width; + if(emb.hasOwnProperty("thumbnail_height")) + embd.thumbnail.height = emb.thumbnail_height; + if(emb.hasOwnProperty("video_url")) + embd.video.url = emb.video_url; + if(emb.hasOwnProperty("video_width")) + embd.video.width = emb.video_width; + if(emb.hasOwnProperty("video_height")) + embd.video.height = emb.video_height; + if(emb.hasOwnProperty("provider_name")) + embd.provider.name = emb.provider_name; + if(emb.hasOwnProperty("provider_url")) + embd.provider.url = emb.provider_url; + if(emb.hasOwnProperty("author_name")) + embd.author.name = emb.author_name; + if(emb.hasOwnProperty("author_url")) + embd.author.url = emb.author_url; + if(emb.hasOwnProperty("author_icon_url")) + embd.author.icon_url = emb.author_icon_url; + + if(emb.hasOwnProperty("save")) + { + if(emb.save.toLowerCase() == "yes") + { + this.settings = $.extend({}, embd); + BotNeckAPI.SaveConfig(); + } + delete emb["save"]; + } + if(embd.timestamp == "now") + embd.timestamp = new Date().toISOString(); + else + embd.timestamp = new Date(embd.timestamp).toISOString(); + let final = {}; + for(let e in embd) + { + if(embd[e] != "" && embd[e] != 0) + { + if(typeof(embd[e]) == "object") + { + final[e] = {}; + for(let i in embd[e]) + { + if(embd[e][i] != "" && embd[e][i] != 0) + { + final[e][i] = embd[e][i]; + } + } + if(Object.keys(final[e]).length === 0) + delete final[e]; + } + else + { + final[e] = embd[e]; + } + } + } + + delete msg["content"]; + msg["embed"] = final; +}; diff --git a/BotNeck_Modules/reload.module.js b/BotNeck_Modules/reload.module.js index 91b2ba4..057b619 100644 --- a/BotNeck_Modules/reload.module.js +++ b/BotNeck_Modules/reload.module.js @@ -9,6 +9,7 @@ reload.prototype.execute = function(msg) { BotNeckAPI.UnloadModules(); BotNeckAPI.LoadModules(); + BotNeckAPI.LoadConfig(); let emb = { title: "BotNeck Reloader", diff --git a/README.md b/README.md index af3be5b..57c12f1 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ BotNeck Bot offers lots of already built in commands such as: - Text to regional text - Easy module/command reloader - Roll a dice + - Backup/Save the chat + - Create custom embeds(does not support fields) It also has features good for developers such as: @@ -27,6 +29,7 @@ It also has features good for developers such as: - Quick error display in message - Easy to use message editor - Basic API(Not finished) + - Integrated and easy to use configuration BotNeck is also special as it uses a send hook to capture the message before it is ever sent to the server. That means the message is never edited or deleted from the server and is instead captured, modified and sent to the server. This will in turn prevent spamming of messages for users on the server. I am also working on adding more features and commands as time goes on. @@ -44,6 +47,9 @@ API(Not finished) - BotNeckAPI.GetCurrentServerID() = This will get the ID of the currently viewed server - BotNeckAPI.GetCurrentChannelID() = This will get the ID of the currently viewed channel - BotNeckAPI.GetDiscordToken() = This will get the discord token of the user(WARNING: Try to avoid the use of this as much as possible!) + - BotNeckAPI.LoadConfig() = This loads the integrated configuration for BotNeck and applys the config to all loaded modules + - BotNeckAPI.SaveConfig() = This saves the configurations of all the loaded modules and BotNeck itself + - BotNeckAPI.GetParameterValueFromText(text, parameter key) = This gets the parameter value from a parameter key(example: parameter_key="Test" if that is your text then setting the parameter key as parameter_key will return Test) ----------