diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..41a6a32f --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules + +#config files +auth.json +rss.json +alias.json +permissions.json +messagebox.json +config.json +memes.json +meme_channels.json + +# Sound Effect Storage +sfx_files + +# IDE files +.vs +.idea diff --git a/Check-file.js b/Check-file.js deleted file mode 100644 index 6dbdf3db..00000000 --- a/Check-file.js +++ /dev/null @@ -1,12 +0,0 @@ -var fs = require('fs'); -var check = require('syntax-error'); -var file = __dirname + '/plugins/Misc/misc.js'; -var src = fs.readFileSync(file); - -var err = check(src, file); -if (err) { - console.error(Array(62).join('!')); - console.error('ERROR DETECTED'); - console.error(err); - console.error(Array(76).join('_')); -} diff --git a/LICENSE b/LICENSE old mode 100755 new mode 100644 diff --git a/README.md b/README.md new file mode 100644 index 00000000..e3cd28e6 --- /dev/null +++ b/README.md @@ -0,0 +1,146 @@ +# DiscordBot +A chat bot for Discord, built on top of discord.js. + +# Features: + +This bot has many many commands. Here are a few highlights: + +- `!help [command]` -> Lists all commands or just help for one command. +- `!gif query` -> Returns a gif connected to search query. Example = !gif dogs +- `!image query` -> Returns an image from Google Images (careful, no adult filter) Example: `!image dogs` +- `!youtube query` -> Returns a youtube link. Example: `!youtube Fortnite` +- `!wiki query` -> Returns the summary of the first search result on Wikipedia. +- `!wolfram query` -> Queries Wolfram Alpha for results. +- `!meme memetype "text1" "text2"` => Returns a meme image. notice the quotes around text, they are vitally important! +- `!say text` -> Make the bot say text, useful mostly in combination with `alias`. +- `!alias` -> Create custom shorthand commands in Discord! +- `!join-server` -> Bot will join the requested server, easy way to get the bot in multiple servers. +- Channel management! + +Music streaming: +- `!play song` -> Plays the requested song in voice. +- `!skip song` -> Skip currently playing song +- `!queue` -> The queue of songs +- `!queue [number]` -> The removes song from queue + +And much more! Try `!help` to get a full list of available commands. + +# Installation + +This bot runs on [node.js](https://nodejs.org). You will need at least node 12. In order for music playback to work, you will need python and ffmpeg to be present on your system. + +## Linux + +### General + +Install [node 12 or newer]((https://nodejs.org/en/download/)), Python, and FFmpeg, along with a C compiler for npm to use. + +Run `npm install` in the bot directory and make sure it passes. + +Now set up your `auth.json` and run `npm start` or `node discord_bot.js` to test the bot out! + +### Ubuntu + +First install the needed system dependencies: + `sudo apt install build-essential nodejs python ffmpeg` + + Now run `node --version` and make sure it is v12 or later. If not refer to [the node.js download page](https://nodejs.org/en/download/) to get an updated version. + +Run `npm install` in the bot directory and make sure it passes. + +Now set up your `auth.json` and run `npm start` or `node discord_bot.js` to test the bot out! + +## Windows + +1. Install [node.js](https://nodejs.org/en/download/) +2. Install [python](https://www.python.org/) +3. Install [Visual Studio Community](https://visualstudio.microsoft.com/vs/community/) +4. Install [FFmpeg](https://www.ffmpeg.org/download.html) +5. Open `x64 Native Tools Command Prompt for VS 2019` and cd to the bot's folder +6. Run `npm install` and make sure it succeeds +7. Set up your `auth.json` +8. Run `npm start` or `node discord_bot.js` to test the bot out! + +### Additional Resources + +* [Installing Node on Windows](http://blog.teamtreehouse.com/install-node-js-npm-windows) +* [npm errors on Windows](http://stackoverflow.com/questions/21365714/nodejs-error-installing-with-npm) +* [Visual Studio Community 2015](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) +* [Python 2.7](https://www.python.org/downloads/) + +[Tuck 64 was kind enough to make a video walkthrough of the setup process](https://www.youtube.com/watch?v=H-82S2jFOII) + +# Setting up +Before the first run you will need to create an `auth.json` file. A bot token is required. The other credentials are not required for the bot to run, but they are highly recommended as commands that depend on them will not function. See `auth.json.example`. + +[Please see this excellent guide for how to create your discord bot's account and get your bot token.](https://discordjs.guide/preparations/setting-up-a-bot-application.html) + +Verify that the bot runs with your config by running `npm start`. + +# Running longterm +Once you've setup your keys and checked that the features you want are working, you have a couple of options for running the bot. + +## Selfhosted +You could run the bot along side everything else on your pc. However it's probably a good idea to run your bot on a separate computer such as a linux server or a Raspberry Pi so it does not interfere with your normal operations and to keep it running even if you were to sleep or shutdown your PC. +We would recommend running the bot in "forever" mode. +Run `npm run forever` to start the bot in a process that will restart it on crashes. If you need to stop running it, navigate to the bot installation folder in a terminal and run `npx forever stopall`. +Running the bot in this mode will save error and console logs to err.log and out.log respectively. You can use Notepad or similar to open these files. + +## Cloud Hosted +There is a number of cloud hosting providers that can run small node.js applications like this. The following have been tested to work, you'll have to extrapolate if you want to use some other provider (AWS, etc) + +### Running on Heroku +- Create heroku account, install heroku-cli, create a new Dyno. +- Git clone the repo and follow the instructions in the Deploy section to setup pushing to heroku +- Go to settings and setup Config Vars the name of the vars are exactly the same as the auth.json file. You **DO NOT** need the quotes around the values in config vars +- Run `heroku scale worker=1` in the bot installation directory to run the bot as a worker rather than a webserver. +- SOME COMMANDS ARE NOT WORKING, I AM WORKING TO FIX THIS. + +### Running on Repl.it +You will still need to create an `auth.json` file with your credentials with this process, follow the steps above. +[![Run on Repl.it](https://repl.it/badge/github/chalda/DiscordBot)](https://repl.it/github/chalda/DiscordBot) + + + +# FAQ +## Music is always saying "invalid video" +You likely need to update youtube-dl. you can do so with `node ./node_modules/youtube-dl/scripts/download.js` + +## I don't want some of these commands! +Most of our commands live in the plugins folder, deleting that plugin will easily remove the command. Also see permissions below. + +## How do I restrict a command to only certain users? +You will need to set up permissions.json. Please see permissions.json.example. + +## Updates +If you update the bot, please run `npm update` before starting it again. If you have +issues with this, you can try deleting your node_modules folder and then running +`npm install` again. Please see [Installation](#Installation). + +## RSS +You can create an rss.json file adding rss feeds as commands. See rss.json.example for details. + +## Special instructions for setting up google search and youtube APIs: + +(thanks @SchwererKonigstiger) + +1) Create a Custom Search at: https://cse.google.com/cse/create/new + +2) Leave the first line blank, and name the search engine anything you wish. + +3) Click "Advanced Options" and then type ImageObject. + +4) Hit create. + +5) On this new page, enable the Image Search in the menu. + +6) Then press "Search engine ID" under the Details header. + +7) Copy this into the auth.json's "google_custom_search" section. + +Make sure you also have your Google server API key, which is located in the "youtube_api_key" section, or the search will fail. + +# Help +Please check the GitHub issues page on this project. We get a lot of similar questions, and it is likely that yours has already been answered. + +If you still need help, feel free to join us on [Discord](https://discord.gg/m29GJBN). diff --git a/config.json.example b/config.json.example deleted file mode 100644 index 451b44f2..00000000 --- a/config.json.example +++ /dev/null @@ -1,4 +0,0 @@ -{ - "debug": true, - "commandPrefix": "!" -} diff --git a/discord_bot.js b/discord_bot.js new file mode 100755 index 00000000..95a58515 --- /dev/null +++ b/discord_bot.js @@ -0,0 +1,456 @@ +var fs = require('fs'); +var _ = require('lodash'); + +process.on('unhandledRejection', (reason) => { + console.error(reason); + process.exit(1); +}); + +try { + var Discord = require("discord.js"); +} catch (e){ + console.log(e.stack); + console.log(process.version); + console.log("Please run npm install and ensure it passes with no errors!"); // if there is an error, tell to install dependencies. + process.exit(); +} +console.log("Starting DiscordBot\nNode version: " + process.version + "\nDiscord.js version: " + Discord.version); // send message notifying bot boot-up + + +var AuthDetails = require('./auth.js').getAuthDetails() + +if(!AuthDetails.hasOwnProperty('bot_token') || AuthDetails.bot_token === '') { + console.error("Please create an auth.json or specify environmental variables, the bot cannot run without a bot_token"); // send message for error - no token + process.exit() +} + +// Load custom permissions +var dangerousCommands = ["eval","pullanddeploy","setUsername","cmdauth"]; // set array if dangerous commands +var Permissions = {}; +try{ + Permissions = require("./permissions.json"); +} catch(e){ + Permissions.global = {}; + Permissions.users = {}; +} + +for( var i=0; i { + if(err) console.error(err); +}); + +//load config data +var Config = {}; +try{ + Config = require("./config.json"); +} catch(e){ //no config file, use defaults + Config.debug = false; + Config.commandPrefix = '!'; + try{ + if(fs.lstatSync("./config.json").isFile()){ // open config file + console.log("WARNING: config.json found but we couldn't read it!\n" + e.stack); // corrupted config file + } + } catch(e2){ + fs.writeFile("./config.json",JSON.stringify(Config,null,2), (err) => { + if(err) console.error(err); + }); + } +} +if(!Config.hasOwnProperty("commandPrefix")){ + Config.commandPrefix = '!'; // set bots prefix +} + +var messagebox; +var aliases; +try{ + aliases = require("./alias.json"); +} catch(e) { + //No aliases defined + aliases = {}; +} + +commands = { // all commands list below + "alias": { + usage: " ", + description: "Creates command aliases. Useful for making simple commands on the fly.", + process: function(bot,msg,suffix) { + var args = suffix.split(" "); + var name = args.shift(); + if(!name){ + msg.channel.send(Config.commandPrefix + "alias " + this.usage + "\n" + this.description); + } else if(commands[name] || name === "help"){ + msg.channel.send("overwriting commands with aliases is not allowed!"); + } else { + var command = args.shift(); + aliases[name] = [command, args.join(" ")]; + //now save the new alias + require("fs").writeFile("./alias.json",JSON.stringify(aliases,null,2), null); + msg.channel.send("created alias " + name); + } + } + }, + "aliases": { + description: "Lists all recorded aliases.", + process: function(bot, msg, suffix) { + var text = "current aliases:\n"; + for(var a in aliases){ + if(typeof a === 'string') + text += a + " "; + } + msg.channel.send(text); + } + }, + "ping": { + description: "Responds pong; useful for checking if bot is alive.", + process: function(bot, msg, suffix) { + msg.channel.send( msg.author+" pong!"); + if(suffix){ + msg.channel.send( "Note that !ping takes no arguments!"); + } + } + }, + "idle": { + description: "Sets bot status to idle.", + process: function(bot,msg,suffix){ + bot.user.setStatus("idle").then(console.log).catch(console.error); + } + }, + "online": { + description: "Sets bot status to online.", + process: function(bot,msg,suffix){ + bot.user.setStatus("online").then(console.log).catch(console.error); + } + }, + "say": { + usage: "", + description: "Bot sends message", + process: function(bot,msg,suffix){ msg.channel.send(suffix);} + }, + "announce": { + usage: "", + description: "Bot sends message in text to speech.", + process: function(bot,msg,suffix){ msg.channel.send(suffix,{tts:true});} + }, + "msg": { + usage: " ", + description: "Sends a message to a user the next time they come online.", + process: function(bot,msg,suffix) { + var args = suffix.split(' '); + var user = args.shift(); + var message = args.join(' '); + if(user.startsWith('<@')){ + user = user.substr(2,user.length-3); + } + var target = msg.channel.guild.members.fetch({query:user, limit:1}); + target.then((result)=>{ + messagebox[target.id] = { + channel: msg.channel.id, + content: target + ", " + msg.author + " said: " + message + }; + updateMessagebox(); + msg.channel.send("Message saved.") + }).catch(console.error) + + } + }, + "eval": { + usage: "", + description: 'Executes arbitrary javascript in the bot process. User must have "eval" permission.', + process: function(bot,msg,suffix) { + let result = eval(suffix,bot).toString(); + if(result) { + msg.channel.send(result); + } + } + }, + "cmdauth": { + usage: " ", + description: "Gets/toggles command usage permissions for the specified user.", + process: function(bot,msg,suffix) { + var Permissions = require("./permissions.json"); + var fs = require('fs'); + + var args = suffix.split(' '); + var userid = args.shift(); + var action = args.shift(); + var cmd = args.shift(); + + if(userid.startsWith('<@')){ + userid = userid.substr(2,userid.length-3); + } + + var target = msg.channel.guild.members.find("id",userid); + if(!target) { + msg.channel.send("Could not find user."); + } else { + if(commands[cmd] || cmd === "*") { + var canUse = Permissions.checkPermission(userid,cmd); + var strResult; + if(cmd === "*") { + strResult = "All commands" + } else { + strResult = 'Command "' + cmd + '"'; + } + if(action.toUpperCase() === "GET") { + msg.channel.send("User permissions for " + strResult + " are " + canUse); + } else if(action.toUpperCase() === "TOGGLE") { + if(Permissions.users.hasOwnProperty(userid)) { + Permissions.users[userid][cmd] = !canUse; + } + else { + Permissions.users[userid].append({[cmd] : !canUse}); + } + fs.writeFile("./permissions.json",JSON.stringify(Permissions,null,2)); + + msg.channel.send("User permission for " + strResult + " set to " + Permissions.users[userid][cmd]); + } else { + msg.channel.send('Requires "get" or "toggle" parameter.'); + } + } else { + msg.channel.send("Invalid command.") + } + } + } + } +}; + +if(AuthDetails.hasOwnProperty("client_id")){ + commands["invite"] = { + description: "Generates an invite link you can use to invite the bot to your server.", + process: function(bot,msg,suffix){ + msg.channel.send("Invite link: https://discordapp.com/oauth2/authorize?&client_id=" + AuthDetails.client_id + "&scope=bot&permissions=470019135"); // send link to invite bot into server. + } + } +} + + +try{ + messagebox = require("./messagebox.json"); +} catch(e) { + //no stored messages + messagebox = {}; +} +function updateMessagebox(){ + require("fs").writeFile("./messagebox.json",JSON.stringify(messagebox,null,2), null); +} + +var bot = new Discord.Client(); + +var hooks = { + onMessage: [] +} + +bot.on("ready", function () { + require("./plugins.js").init(hooks); + console.log("Logged in! Currently serving " + bot.guilds.cache.array().length + " servers."); + bot.user.setPresence({ + activity: { + name: Config.commandPrefix+"help | " + bot.guilds.cache.array().length +" Servers" + } + }); + console.log("Type "+Config.commandPrefix+"help on Discord for a command list."); + +}); + +bot.on("disconnected", function () { + + console.log("Disconnected!"); // send message that bot has disconnected. + process.exit(1); //exit node.js with an error + +}); + +function checkMessageForCommand(msg, isEdit) { + //check if message is a command + if(msg.author.id != bot.user.id && (msg.content.startsWith(Config.commandPrefix))){ + console.log("treating " + msg.content + " from " + msg.author + " as command"); + var cmdTxt = msg.content.split(/\s/)[0].substring(Config.commandPrefix.length); + var suffix = msg.content.substring(cmdTxt.length+Config.commandPrefix.length+1);//add one for the ! and one for the space + if(msg.mentions.has(bot.user)){ + try { + cmdTxt = msg.content.split(/\s/)[1]; + suffix = msg.content.substring(bot.user.mention().length+cmdTxt.length+Config.commandPrefix.length+1); + } catch(e){ //no command + //msg.channel.send("Yes?"); + return false; + } + } + alias = aliases[cmdTxt]; + if(alias){ + console.log(cmdTxt + " is an alias, constructed command is " + alias.join(" ") + " " + suffix); + cmdTxt = alias[0]; + suffix = alias[1] + " " + suffix; + } + var cmd = commands[cmdTxt]; + if(cmdTxt === "help"){ + //help is special since it iterates over the other commands + if(suffix){ + var cmds = suffix.split(" ").filter(function(cmd){return commands[cmd]}); + var info = ""; + for(var i=0;i 0){ + msg.channel.send(info); + } else { + msg.channel.send(`no command ${suffix}`); + } + } else { + msg.author.send("**Available Commands:**").then(function(){ + var batch = ""; + var sortedCommands = Object.keys(commands).sort(); + for(var i in sortedCommands) { + var cmd = sortedCommands[i]; + var info = "**"+Config.commandPrefix + cmd+"**"; + var usage = commands[cmd].usage; + if(usage){ + info += " " + usage; + } + var description = commands[cmd].description; + if(description instanceof Function){ + description = description(); + } + if(description){ + info += "\n\t" + description; + } + var newBatch = batch + "\n" + info; + if(newBatch.length > (1024 - 8)){ //limit message length + msg.author.send(batch); + batch = info; + } else { + batch = newBatch + } + } + if(batch.length > 0){ + msg.author.send(batch); + } + }); + } + return true; + } + else if(cmd) { + if(Permissions.checkPermission(msg.author.id,cmdTxt)){ + try{ + cmd.process(bot,msg,suffix,isEdit); + } catch(e){ + var msgTxt = "command " + cmdTxt + " failed :("; + console.error(e); + if(Config.debug){ + msgTxt += "\n" + e.stack; + console.log(msgTxt); + } + if(msgTxt.length > (1024 - 8)){ //Truncate the stack if it's too long for a discord message + msgTxt = msgTxt.substr(0,1024-8); + } + msg.channel.send(msgTxt); + } + } else { + msg.channel.send("You are not allowed to run " + cmdTxt + "!"); + } + return true; + } else { + msg.channel.send(cmdTxt + " is not recognized as a command!").then((message => message.delete({timeout: 5000}))) + return true; + } + } else { + //message is not a command or is from us + //drop our own messages to prevent feedback loops + if(msg.author == bot.user){ + return true; //returning true to prevent feedback from commands + } + + if (msg.author != bot.user && msg.mentions.has(bot.user)) { + //msg.channel.send("yes?"); //using a mention here can lead to looping + } else { + + } + return false; + } +} + +bot.on("message", (msg) => { + if(!checkMessageForCommand(msg, false)){ + for(msgListener of hooks.onMessage){ + msgListener(msg); + } + } +}); +bot.on("messageUpdate", (oldMessage, newMessage) => { + checkMessageForCommand(newMessage,true); +}); + +//Log user status changes +bot.on("presence", function(user,status,gameId) { + //if(status === "online"){ + //console.log("presence update"); + console.log(user+" went "+status); + //} + try{ + if(status != 'offline'){ + if(messagebox.hasOwnProperty(user.id)){ + console.log("Found message for " + user.id); + var message = messagebox[user.id]; + var channel = bot.channels.get("id",message.channel); + delete messagebox[user.id]; + updateMessagebox(); + bot.send(channel,message.content); + } + } + }catch(e){} +}); + + +exports.addCommand = function(commandName, commandObject){ + try { + commands[commandName] = commandObject; + } catch(err){ + console.log(err); + } +} +exports.commandCount = function(){ + return Object.keys(commands).length; +} +if(AuthDetails.bot_token){ + console.log("logging in with token"); + bot.login(AuthDetails.bot_token); +} else { + console.log("Logging in with user credentials is no longer supported!\nYou can use token based log in with a user account; see\nhttps://discord.js.org/#/docs/main/master/general/updating."); +} diff --git a/guilds/.DS_Store b/guilds/.DS_Store deleted file mode 100755 index 7cf46605..00000000 Binary files a/guilds/.DS_Store and /dev/null differ diff --git a/guilds/[null] b/guilds/[null] deleted file mode 100755 index ecaf43d2..00000000 --- a/guilds/[null] +++ /dev/null @@ -1 +0,0 @@ -Just a placeholder to keep folder in place (don’t mind me) diff --git a/package.json b/package.json new file mode 100644 index 00000000..11d7fce1 --- /dev/null +++ b/package.json @@ -0,0 +1,58 @@ +{ + "name": "DiscordBot", + "version": "0.2.0", + "description": "Bot for Discord app", + "readme": "README.md", + "maintainers": [ + { + "name": "Alex Chaldyshev", + "url": "https://github.com/chalda" + }, + { + "name": "Johnny Dickinson", + "url": "https://github.com/Einarin" + } + ], + "author": "Alex Chaldyshev", + "repository": { + "type": "git", + "url": "git+https://github.com/chalda/DiscordBot.git" + }, + "license": "GPL-2.0", + "dependencies": { + "@discordjs/opus": "0.3.2", + "axios": "0.20.0", + "bufferutil": "^4.0.1", + "cheerio": "^1.0.0-rc.3", + "d20": "^1.4.1", + "discord.js": "12.2.0", + "feedparser": "1.1.x", + "ffmpeg-static": "^4.2.7", + "forever": "3.0.2", + "html-to-text": "^3.3.0", + "imgflipper": "^1.0.1", + "leet": "^1.3.0", + "lodash": "^4.17.20", + "memorystream": "0.3.1", + "node-wolfram": "0.0.1", + "npm": "^6.14.8", + "querystring": "0.2.x", + "request": "2.88.2", + "request-promise": "4.2.1", + "split-camelcase-to-words": "^1.0.1", + "string-sanitizer": "^1.1.1", + "tumblr.js": "0.0.x", + "urban": "^0.3.1", + "utf-8-validate": "^5.0.2", + "uws": "^100.0.1", + "wikijs": "^6.0.1", + "youtube-dl": "^3.0.2", + "youtube-node": "^1.3.3" + }, + "main": "discord_bot.js", + "scripts": { + "start": "node ./discord_bot.js", + "forever": "forever -o out.log -e err.log -n 10000 start ./discord_bot.js", + "postinstall": "node ./node_modules/youtube-dl/scripts/download.js" + } +} diff --git a/permissions.json.example b/permissions.json.example index 8d179e61..bedd8442 100644 --- a/permissions.json.example +++ b/permissions.json.example @@ -11,16 +11,17 @@ "setUsername": false, "cmdauth": false }, - "users":{ - "userid":{ - "eval": false, - "pullanddeploy": true - } - }, - "roles":{ - "roleid":{ - "eval": true, - "pullanddeploy": false - } + "users": { + "12300000000000004": { + "eval": true, + "cmdauth": true, + "*": true + }, + "56700000000000008": { + "eval": false, + "say": false, + "cmdauth": false, + "year_fact": false + } } } diff --git a/plugins.js b/plugins.js old mode 100755 new mode 100644 diff --git a/plugins/.DS_Store b/plugins/.DS_Store deleted file mode 100644 index 04f17c3d..00000000 Binary files a/plugins/.DS_Store and /dev/null differ diff --git a/plugins/Admin/admin.js b/plugins/Admin/admin.js old mode 100755 new mode 100644 index 71cb2de3..056c6ec8 --- a/plugins/Admin/admin.js +++ b/plugins/Admin/admin.js @@ -1,96 +1,51 @@ exports.commands = [ - "setUsername", - "log", - "uptime", - "perm" + "setUsername", + "log", + "uptime" ] -var fs = require("fs"); var startTime = Date.now(); exports.setUsername = { - description: "sets the username of the bot. Note this can only be done twice an hour!", - process: function(bot,msg,suffix) { - bot.user.setUsername(suffix); - } + description: "sets the username of the bot. Note this can only be done twice an hour!", + process: function(bot,msg,suffix) { + bot.user.setUsername(suffix); + } } exports.log = { - usage: "", - description: "logs message to bot console", - process: function(bot,msg,suffix){console.log(msg.content);} + usage: "", + description: "logs message to bot console", + process: function(bot,msg,suffix){console.log(msg.content);} } exports.uptime = { - usage: "", - description: "returns the amount of time since the bot started", - process: function(bot,msg,suffix){ - var now = Date.now(); - var msec = now - startTime; - console.log("Uptime is " + msec + " milliseconds"); - var days = Math.floor(msec / 1000 / 60 / 60 / 24); - msec -= days * 1000 * 60 * 60 * 24; - var hours = Math.floor(msec / 1000 / 60 / 60); - msec -= hours * 1000 * 60 * 60; - var mins = Math.floor(msec / 1000 / 60); - msec -= mins * 1000 * 60; - var secs = Math.floor(msec / 1000); - var timestr = ""; - if(days > 0) { - timestr += days + " days "; - } - if(hours > 0) { - timestr += hours + " hours "; - } - if(mins > 0) { - timestr += mins + " minutes "; - } - if(secs > 0) { - timestr += secs + " seconds "; - } - msg.channel.send("**Uptime**: " + timestr); - } -} -exports.perm = { - usage: "set or rm ", - description: "Used to set(add) or remove permissions from a @user or @role", - process: function(bot,msg,arg){ - var args = arg.split(" "); - var permsFile = require("../../permissions.json"); - var user, role; - if(args[0] == "set"){ - if(typeof args[1] != 'undefined' && typeof args[2] != 'undefined' && typeof args[3] != 'undefined'){ - var node1 = args[2]; - var perm1; - if(args[3] == "true"){ perm1 = true; }else{ perm1 = false; } - try{ - if(msg.mentions.users.first()){ console.log('User mention'); user = msg.mentions.users.first().id; } - if(msg.mentions.roles.first()){ console.log('Role mention '+msg.mentions.roles.first().id); role = msg.mentions.roles.first().id; } - }catch(err){ console.log('Error occured at: '+err+' Mentions.first() is undefined'); } - if(typeof user != 'undefined'){ - if(typeof permsFile.users[user] == 'undefined'){ permsFile.users[user] = {[node1]:perm1}; } - else{ permsFile.users[user][node1] = perm1; } - }else if(typeof role != 'undefined'){ - if(typeof permsFile.roles[role] == 'undefined'){ permsFile.roles[role] = {[node1]:perm1}; } - else{ permsFile.roles[role][node1] = perm1; } - } - }else{ msg.channel.send('__Parameters can\'t be left blank: '+typeof args[1]+', '+typeof args[2]+', '+typeof args[3]+'__'); } - }else if(args[0] == "rm"){ - if(typeof args[1] != 'undefined' && typeof args[2] != 'undefined'){ - var node1 = args[2]; - try{ - if(msg.mentions.users.first()){ user = msg.mentions.users.first().id; } - if(msg.mentions.roles.first()){ role = msg.mentions.roles.first().id; } - }catch(err){ console.log('Error occured at: '+err+' Mentions.first() is undefined'); } - if(typeof user != 'undefined'){ delete permsFile.users[user][node1]; } - else if(typeof role != 'undefined'){ delete permsFile.roles[role][node1]; } - }else{ msg.channel.send('__Parameters can\'t be left blank: '+typeof args[1]+', '+typeof args[2]+', '+typeof args[3]+'__'); } - }else{ msg.channel.send("__Too few Arguments: either add or rm.__"); } - try{ - if(fs.lstatSync("./permissions.json").isFile()){ console.log("WARNING: permissions.json found but we couldn't read it!\n" + e.stack); } - } catch(e2){ - if (!fs.writeFile("./permissions.json",JSON.stringify(permsFile,null,2))){ - //console.log('Write success!'); - }} - } -} + usage: "", + description: "returns the amount of time since the bot started", + process: function(bot,msg,suffix){ + var now = Date.now(); + var msec = now - startTime; + console.log("Uptime is " + msec + " milliseconds"); + var days = Math.floor(msec / 1000 / 60 / 60 / 24); + msec -= days * 1000 * 60 * 60 * 24; + var hours = Math.floor(msec / 1000 / 60 / 60); + msec -= hours * 1000 * 60 * 60; + var mins = Math.floor(msec / 1000 / 60); + msec -= mins * 1000 * 60; + var secs = Math.floor(msec / 1000); + var timestr = ""; + if(days > 0) { + timestr += days + " days "; + } + if(hours > 0) { + timestr += hours + " hours "; + } + if(mins > 0) { + timestr += mins + " minutes "; + } + if(secs > 0) { + timestr += secs + " seconds "; + } + msg.channel.send("**Uptime**: " + timestr); + } +} \ No newline at end of file diff --git a/plugins/Moderation/moderation.js b/plugins/Moderation/moderation.js old mode 100755 new mode 100644 index a220f47a..2e23d489 --- a/plugins/Moderation/moderation.js +++ b/plugins/Moderation/moderation.js @@ -1,8 +1,8 @@ //None of these commands actually work. Disabling them for now exports.commands = [ "myid", - "permission", - "votekick", + //"perm", + //"votekick", "kick" ] @@ -54,7 +54,7 @@ exports.myid = { process: function(bot,msg){msg.channel.send(msg.author.id);} } -exports.permission = { +exports.perm = { usage: "[user]", description: "Returns the user's permissions in this channel", process: function(bot,msg,suffix) { diff --git a/plugins/Multi Stream/package.json b/plugins/Multi Stream/package.json deleted file mode 100644 index ce7b0b87..00000000 --- a/plugins/Multi Stream/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "main": "stream.js", - "dependencies": { - "request": ">=2.58.0", - "jquery": "3.2.x", - "jsdom": "11.x.x" - } -} diff --git a/plugins/Multi Stream/stream.js b/plugins/Multi Stream/stream.js deleted file mode 100644 index cc630abe..00000000 --- a/plugins/Multi Stream/stream.js +++ /dev/null @@ -1,376 +0,0 @@ -/* File: stream.js - * Description: Used to semi automate the creation of multistre.am links (mainly for the community I am apart of) - * Author: Fang_Shadow - * Date: 12/11/17 - * Estimated Time worked: 9 Hours - */ -exports.commands = [ - "multi", //Contains all logic and commands for multi - "multi_set_community", //information holder only - "multi_set_client", //Information in help only - "multi_set_secret", //Information in help only - "MTP" //Used to add a Channel to timed purge (~15 minute old messages are deleted) -]; -const { JSDOM } = require("jsdom"); -const jquery = require("jquery"); -var fs = require("fs"); -var append; - - -// global envs for JSDOM and JQuery -global.document = new JSDOM(''); -global.$ = jquery(global.document.window); -var Stream_data = {}; -var User_data = {}; -var link_count = 0; - -exports.multi_set_community = { //forces !multi set community to show - usage: "", - description: "Enter the community name to set it." -} -exports.multi_set_client = { //forces !multi set client to show - usage: "", - description: "Enter the client id for the twitch app you registered to set it." -} -exports.multi_set_client = { //forces !multi set client to show - usage: "", - description: "Enter the secret for the twitch app you registered to set it." -} - -exports.multi = { //Where all majick happens - usage: "", - description: "Used to get multi-links for twitch multistre.am. (WIP)", - process: function(bot, msg, arg) { - var Guild = require("../../guilds/"+ msg.guild.name +".json"); - var args = arg.split(" "); - //var args = arg; - if(!arg){ - if(msg.guild != null){ - if(Guild.client_id == ""){ - msg.channel.send("__Client ID not set! Please use !multi set client to do so.__"); - return; - } - if(Guild.secret == ""){ - msg.channel.send("__Secret not set! Please use !multi set secret to do so.__"); - return; - } - if(Guild.commid == "" || Guild.community == ""){ - msg.channel.send("__Community & | Community id not set! Please use !multi set community to do so.__"); - return; - } - link_count = 1; - User_data = {}; - Stream_data = {}; - getStreams(Guild.commid,Guild.client_id,msg); - }else{ - msg.channel.send("__Command needs to be sent from a Server Channel!__"); - } - } else if(args[0] == "set"){ - if(args[1] == "community"){ - if(Guild.client_id == ""){ - msg.channel.send("__Client ID not set! Please use !multi set client to do so.__"); - return; - } - getCommID(msg.channel,Guild.client_id,args[2],function(call){ - if(call != null){ - append = appendJSON(args[2],null,null,null,call._id,msg); - } else { - append = "Failed!"; - } - msg.channel.send("__Community ID: "+append+"__"); - }); - } else if(args[1] == "client"){ - if(msg.guild == null){ - msg.author.send("Please use this command on a Server!"); - return; - }else{ - append = appendJSON(null,args[2],null,null,null,msg); - msg.channel.send("__Client ID: "+append+"__"); - } - } else if(args[1] == "secret"){ - if(msg.guild == null){ - msg.author.send("Please use this command on a server!"); - return; - }else{ - if(Guild.client_id == ""){ - msg.channel.send("__Client ID not set! Please use !multi set client to do so.__"); - } - getAccessKey(Guild.client_id,args[2],function(call){ - if(typeof call.access_token != 'undefined'){ - append = appendJSON(null,null,args[2],call.access_token,null,msg); - } else { - append = "Failed!"; - } - msg.channel.send("__Secret: "+append+"__"); - }); - } - } else if(!args[1]){ - msg.channel.send("__Missing arguments!__"); - } else { - msg.channel.send("__Argument: "+ args[1] +" is inavlid!__"); - } - //} else if(args[0] == "purge"){ - //append = createChannelPurge(msg); - //msg.channel.send("__Channel Timed Purge: "+append+"__"); - } else { - msg.channel.send("__Command "+ arg +", does not exist!__"); - } - - } - -} - -//getCommID - Uses Twitch api v5 (Kraken) to get the community_id from community name -function getCommID (channel, client_id, comm, callback) { - //console.log("Debug: Ajax script getCommID function\n"); //Debug code testing not caught in if statement - $.ajax({ - url: 'https://api.twitch.tv/kraken/communities?name=' + comm, - beforeSend: function(xhr) { - xhr.setRequestHeader("Client-ID", client_id); - xhr.setRequestHeader("Accept", "application/vnd.twitchtv.v5+json"); - }, - type: 'GET', - dataType: 'json', - contentType: 'application/json', - success: function(resp){ var data = resp; callback(data);}, - error: function(err){ - console.log("Error has occured at getComm: "+ err+"\n"); - callback(); - } - }); -} - -function getAccessKey(client, secret, callback) { - console.log('getAccessKey client: '+client); - $.ajax({ - url: 'https://api.twitch.tv/kraken/oauth2/token?client_id='+client+'&client_secret='+secret+'&grant_type=client_credentials&scope=user:read:email', - //beforeSend: function(xhr) { - //xhr.setRequestHeader("Client-ID", client_id); - //xhr.setRequestHeader("Accept", "application/vnd.twitchtv.v5+json"); - //}, - type: 'POST', - dataType: 'json', - contentType: 'application/x-www-form-urlencode', - success: function(resp){ var data = resp; console.log('In success: '+JSON.stringify(data,null,2)); callback(data);}, - error: function(err){ - console.log("Error has occured at getAccessKey: "+ JSON.stringify(err,null,2)+"\n"); - callback(); - } - }); -} - -function getStreams(commid,client,msg,pagination){ - var data; - var response = {}; //Object initilization - var pagination; //undefined only used to make the rest of the function happy - - if(pagination == null){ //Null throws an error on twitch api, so we set it to a blank. - pagination = ""; - } - - $.ajax({ - url: 'https://api.twitch.tv/helix/streams?community_id=' + commid +'&first=8&after='+pagination, - beforeSend: function(xhr) { - xhr.setRequestHeader("Client-ID", client); - }, - method: 'GET', - dataType: 'json', - //data: JSON.parse(data), //Don't know if this helped at all or not. - contentType: 'application/json', - success: function(resp){ //console.log('in success state: getStreams-ajax'); // more debug code when troubleshooting why ajax was having issues - var datas = resp; - pagination = datas.pagination.cursor; - if(typeof datas.pagination.cursor == 'undefined'){ return; - } else { - //Takes the data from ajax and makes it into a string - var dat_string = JSON.stringify(datas,null); - //Parses after being made into a string (Doesn't like what ajax spits out) - var parsed = JSON.parse(dat_string); - var dat_parse = parsed.data; //Shorten the variable needed to be used (dat_parsed ~= parsed.data) - var result = []; //initialize variable containing the array for the culling - for (var i= 0; i < dat_parse.length; i++) { - var obj_ind = dat_parse[i]; - var user_id = obj_ind.user_id; - var type = obj_ind.type; - result.push({'user_id':user_id,'type':type}); - } - Stream_data = {'data':result}; - - getUserNames(Stream_data,client,msg); //Sends off to get more information to make the multistre.am link - getStreams(commid,client,msg,pagination); //Calls current function with Pagination to being up next grouping of results (if there are any) - }}, - error: function(err){ - console.log("Error has occured at getStreams: "+ JSON.stringify(err,null,2)+"\n"); - } - }); -} - -function getUserNames(data,client,msg){ - var Bearer = require('../../guilds/'+msg.guild.name+'.json').bearer; - var id = ""; - var response = []; - id = data.data[0].user_id; - for(var i = 1; i < data.data.length; i++){ - id += "&id="+data.data[i].user_id; - } - - $.ajax({ - url: 'https://api.twitch.tv/helix/users?id=' + id, - beforeSend: function(xhr) { - xhr.setRequestHeader("Client-ID", client); - xhr.setRequestHeader("Authorization","Bearer "+Bearer); - }, - type: 'GET', - dataType: 'json', - contentType: 'application/json', - success: function(resp){ - datas = resp; - - var dat_string = JSON.stringify(datas,null); - //Parses after being made into a string (Doesn't like what ajax spits out) - var parsed = JSON.parse(dat_string); - var dat_parse = parsed.data; //Shorten the variable needed to be used (dat_parsed ~= parsed.data) - var result = []; //initialize variable containing the array for the culling - - //culls information we don't need (id, profile pic, game, info, description, ect.) - for (var i= 0; i < dat_parse.length; i++) { - var obj_ind = dat_parse[i]; - var user_id = obj_ind.display_name; - response.push({'name':user_id}); - } - - User_data = {'data':response,'link':'Link '+link_count}; - formatLink(User_data,msg); - link_count +=1; - }, - error: function(err){ - console.log("Error has occured at getUserNames: "+ err+"\n"); - } - }); -} - -function formatLink(stream,msg){ - //console.log("Attempt to format: formatLink"); - var baseurl = "http://multistre.am/"; - for(var i = 0; i < stream.data.length; i++){ - baseurl += stream.data[i].name+'/'; - } - msg.channel.send("**"+ stream.link +" "+baseurl+" ** @everyone"); - //console.log("Length of 'Streamers': "+User_data.length); -} - -//Used to append new information to [guild].json file -function appendJSON (community,client,secret,access,commid,msg){ - var Up_Rec = require("../../guilds/"+ msg.guild.name +".json"); - if (client != null){ - Up_Rec.client_id = client; - } - if(secret != null){ - Up_Rec.secret = secret; - } - if(access != null){ - Up_Rec.bearer = access; - } - if (community != null){ - Up_Rec.community = community; - } - if (commid != null){ - Up_Rec.commid = commid; - } - try{ - if(fs.lstatSync("./guilds/"+ msg.guild.name +".json").isFile()){ - console.log("WARNING: "+msg.guild.name+".json found but we couldn't read it!\n" + e.stack); - return "Failed!"; - } - } catch(e2){ - if (!fs.writeFile("./guilds/"+ msg.guild.name +".json",JSON.stringify(Up_Rec,null,2))){ - return "Success!"; - } - return "Failed!"; - } -} - -exports.MTP = { - ussage: "add or rm", - description: "add or remove a channel from a timed purge of 15 minute or older messages __**MuST BE RAN FROM CHANNEL YOU WANT ADDED**__", - process: function(bot,msg,arg){ - var args = arg; - var append; - if (args == "add"){ - append = createChannelPurge(msg); - msg.channel.send("__Add Channel to Purge: "+append+"__"); - }else if(args == "rm"){ - append = deleteChannelPurge(msg); - msg.channel.send("__Remove Channel from Purge: "+append+"__"); - } -} -} -function createChannelPurge(msg){ - var channelObj = msg.channel; - var guildName = msg.guild.name; - var Up_Rec = require("../../guilds/"+guildName+".json"); - var purge = []; - //console.log('Purge type: '+typeof Up_Rec.purge); - if(typeof Up_Rec.purge != 'undefined' && Up_Rec.purge != ""){ - //console.log(JSON.stringify(Up_Rec.purge,null,2)); - //var tmp = JSON.stringify(Up_Rec.purge); - //Up_Rec.purge = []; - //console.log('length of purge: '+Up_Rec.purge.length); - for(let meh of Up_Rec.purge){ - if(msg.channel.name != meh.channel){ - purge.push(meh); - } - } - purge.push({'channel':msg.channel.name}); - Up_Rec.purge = purge; - }else{ - Up_Rec.purge = [{'channel':msg.channel.name}]; - } - - try{ - if(fs.lstatSync("./guilds/"+ guildName +".json").isFile()){ - console.log("WARNING: "+guildName+".json found but we couldn't read it!\n" + e.stack); - return "Failed!"; - } - } catch(e2){ - if (!fs.writeFile("./guilds/"+ guildName +".json",JSON.stringify(Up_Rec,null,2))){ - return "Success!"; - } - return "Failed!"; - } -} - -function deleteChannelPurge(msg){ - var channelObj = msg.channel; - var guildName = msg.guild.name; - var Up_Rec = require("../../guilds/"+guildName+".json"); - var purge = []; - //console.log('Purge type: '+typeof Up_Rec.purge); - if(typeof Up_Rec.purge != 'undefined' && Up_Rec.purge != ""){ - //console.log(JSON.stringify(Up_Rec.purge,null,2)); - //var tmp = JSON.stringify(Up_Rec.purge); - //Up_Rec.purge = []; - //console.log('length of purge: '+Up_Rec.purge.length); - for(let meh of Up_Rec.purge){ - if(msg.channel.name != meh.channel){ - purge.push(meh); - } - } - //purge.push({'channel':msg.channel.name}); - Up_Rec.purge = purge; - }else{ - delete Up_Rec.purge; - } - - try{ - if(fs.lstatSync("./guilds/"+ guildName +".json").isFile()){ - console.log("WARNING: "+guildName+".json found but we couldn't read it!\n" + e.stack); - return "Failed!"; - } - } catch(e2){ - if (!fs.writeFile("./guilds/"+ guildName +".json",JSON.stringify(Up_Rec,null,2))){ - return "Success!"; - } - return "Failed!"; - } -}