diff --git a/BotNeck.plugin.js b/BotNeck.plugin.js index afda8b6..7e17dd7 100644 --- a/BotNeck.plugin.js +++ b/BotNeck.plugin.js @@ -15,12 +15,16 @@ const protectedObject = { // Protected, do not expose to modules "usedKeys": [], "lastUserMessageId": null, "lastBotMessageId": null, - "messagePostEvent": [] + "messagePostEvent": [], + "currentUser": null } const modules = {} // key: BotNeckModule +let botneckConfig = null; + /* Permissions: * - authorized_request = Create a request with your Discord token attached + * - get_current_user_info = Returns current user ID */ function makeKey() { @@ -44,11 +48,11 @@ class BotNeck { load() { // Get paths this.modulesPath = BotNeckAPI.getModulesPath(); - this.botneckConfig = path.join(window.bdConfig.dataPath, "BotNeck.config.json"); + botneckConfig = path.join(window.bdConfig.dataPath, "BotNeck.config.json"); // Load config - if(fs.existsSync(this.botneckConfig)) { - fs.readFile(this.botneckConfig, (err, data) => { + if(fs.existsSync(botneckConfig)) { + fs.readFile(botneckConfig, (err, data) => { if(err) { console.error("Error while reading configuration"); console.error(err); @@ -68,14 +72,9 @@ class BotNeck { return; } }); - } else { - fs.writeFile(this.botneckConfig, JSON.stringify(config), err => { - if(err) { - console.error("Failed to save configuration file"); - console.error(err); - } - }); } + else + BotNeckInternals.saveConfig(); // Load modules if(!fs.existsSync(this.modulesPath)) @@ -137,6 +136,37 @@ class BotNeckAPI { static getLastUserMessageId() { return protectedObject["lastUserMessageId"]; } static getLastBotMessageId() { return protectedObject["lastBotMessageId"]; } static getModulesPath() { return path.join(window.ContentManager.pluginsFolder, "BotNeckModules"); } + static getCurrentUser(apiKey, callback) { + if(!modules[apiKey] || !modules[apiKey].permissions.includes('get_current_user_info')) + return callback(null); + + // Get current user information + if(!protectedObject['currentUser']) { + fetch('https://discordapp.com/api/v6/users/@me', { + method: 'GET', + headers: { + Authorization: protectedObject['token'] + } + }) + .then(res => { + res.json() + .then(user => { + protectedObject['currentUser'] = user; + callback(user); + }) + .catch(err => { + console.log('Failed parsing user information!', err); + callback(null); + }); + }) + .catch(err => { + console.log('Error while getting user information!', err); + callback(null); + }); + return; + } + return callback(protectedObject['currentUser']); + } static setAuthHeader(req, apiKey) { if(!modules[apiKey] || !modules[apiKey].permissions.includes("authorized_request")) @@ -213,6 +243,10 @@ class BotNeckInternals { parsed["embed"] = BotNeckInternals.generateReload(args); delete parsed["content"]; } + else if(command === 'config') { + parsed['embed'] = BotNeckInternals.generateConfigChange(args); + delete parsed['content']; + } else { parsed["embed"] = BotNeckAPI.generateError(`Command *"${command}"* not found! Use the *help* command to see all commands!`); delete parsed["content"]; @@ -311,12 +345,32 @@ class BotNeckInternals { return Number(value); } + static generateConfigChange(args) { + if(BotNeckAPI.getArgumentNumber(args) < 2) { + return { + title: 'BotNeck Config', + type: 'rich', + description: 'Current configuration keys: ' + Object.keys(config).join(', '), + color: 0x0061ff + } + } + + config[args[0]] = args[1]; + this.saveConfig(); + return { + title: 'BotNeck Config', + type: 'rich', + description: 'New configuration set!', + color: 0x0061ff + } + } static generateHelp(args) { if(!args[0]) { // No command provided let help = [ "help - Displays the description of all commands or one command", "usage - Displays the usage of all commands or one command", - "reload - Reloads all the modules or one specific module" + "reload - Reloads all the modules or one specific module", + "config - Sets the specified configuration key to a specific value or displays all configuration keys" ] for(let mod of Object.values(modules)) help.push(`${mod.command} - ${mod.module.description ? mod.module.description : "No description!"}`); @@ -353,6 +407,14 @@ class BotNeckInternals { color: 0x0061ff } } + else if(args[0] === 'config') { + return { + title: "BotNeck Help", + type: "rich", + description: "config - Sets the specified configuration key to a specific value or displays all configuration keys", + color: 0x0061ff + } + } else { let mod = BotNeckInternals.getModuleByCommand(args[0]); @@ -371,7 +433,8 @@ class BotNeckInternals { let usage = [ "help - help *[command]*", "usage - usage *[command]*", - "reload - reload *[command]*" + "reload - reload *[command]*", + "config - config *[config key]* *[config value]*" ] for(let mod of Object.values(modules)) @@ -409,6 +472,14 @@ class BotNeckInternals { color: 0x0061ff } } + else if(args[0] === "config") { + return { + title: "BotNeck Usage", + type: "rich", + description: "config - config *[config key]* *[config value]*", + color: 0x0061ff + } + } else { let mod = BotNeckInternals.getModuleByCommand(args[0]); @@ -525,6 +596,14 @@ class BotNeckInternals { modules[sandbox.key] = new BotNeckModule(sandbox); return true; } + static saveConfig() { + fs.writeFile(botneckConfig, JSON.stringify(config), err => { + if(err) { + console.error("Failed to save configuration file"); + console.error(err); + } + }); + } } class BotNeckSandBox { constructor() { diff --git a/BotNeckModules/eff.botneck.js b/BotNeckModules/eff.botneck.js new file mode 100644 index 0000000..c87a6ce --- /dev/null +++ b/BotNeckModules/eff.botneck.js @@ -0,0 +1,39 @@ +class eff { + constructor() { + this.permissions = [ 'get_current_user_info', 'authorized_request' ]; + this.command = "f"; + this.description = "Press F to pay respects"; + this.usage = "f "; + } + sendMessage(message) { + $.ajax({ + type: "POST", + url: "https://discordapp.com/api/v6/channels/" + BotNeckAPI.getCurrentChannelId() + "/messages", + dataType: "json", + contentType: "application/json", + data: JSON.stringify({ + embed: { + description: message, + color: 0x0061ff, + timestamp: new Date().toISOString() + } + }), + beforeSend: function(xhr) { BotNeckAPI.setAuthHeader(xhr, APIKey); }, + success: function(data) {}, + }); + } + + execute(message, args) { + BotNeckAPI.getCurrentUser(APIKey, user => { + if(!user) return console.log('No current user found! Aborting...'); + + // Send message + if(BotNeckAPI.getArgumentNumber(args) > 0) + this.sendMessage(`**${user.username}** has paid their respect for **${BotNeckAPI.getArgumentsAsString(args)}**`); + else + this.sendMessage(`**${user.username}** has paid their respect`); + }); + + message.content = 'F'; + } +} diff --git a/BotNeckModules/embed.botneck.js b/BotNeckModules/embed.botneck.js index 6309453..84dc8d1 100644 --- a/BotNeckModules/embed.botneck.js +++ b/BotNeckModules/embed.botneck.js @@ -30,7 +30,6 @@ class embed { fields: [] } - // Parse the args for(let key in args) { // Only look for keys @@ -67,7 +66,7 @@ class embed { } } else { let field = { - name: keySteps[1], + name: keySteps[1].replace('_', ' '), value: args[key], inline: true } diff --git a/README.md b/README.md index 0757b48..6baf295 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ BotNeck Bot ========== BotNeck Bot is a self-bot that is implemented into discord as a **BandagedDiscord** plugin. It is designed to be dynamic and easy to use. That means it is extremely easy to add your own commands to the bot without having to modify the code! -To use the bot you need to use the prefix "->" if you want to change the prefix the configuration is located in your BetterDiscord directory under the name "BotNeck.config.json" +To use the bot you need to use the prefix "->" if you want to change the prefix the configuration is located in your BetterDiscord directory under the name "BotNeck.config.json" or change it using the config command ---------- @@ -26,6 +26,8 @@ BotNeck Bot offers lots of already built in commands such as: - Browse MyAnimeList in embeds - Browse AniList in embeds - Embed module to create custom embeds + - F in chat + - Change configuration with a command 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. @@ -46,6 +48,7 @@ API - BotNeckAPI.getArgumentNumber(args) - Gets the number of generic arguments specified - BotNeckAPI.getArgumentsAsString(args) - Returns all the arguments as a string - BotNeckAPI.nextMessagePost(function) - Executes function when a new message is successfully sent + - BotNeckAPI.getCurrentUser(apiKey, function) - Returns the current user's information in the callback - APIKey - A variable containing the API key of the module