├── .nvmrc ├── commands ├── quickref │ ├── references │ │ ├── linux.txt │ │ ├── regex.txt │ │ ├── printf.txt │ │ ├── sqljoin.txt │ │ ├── dotnet.txt │ │ ├── style.txt │ │ ├── codeblock.txt │ │ ├── nohello.txt │ │ ├── subnet.txt │ │ └── hotkeys.txt │ └── quickref.js ├── pro │ ├── proTerms.js │ ├── proTerms.txt │ └── pro.js ├── help │ ├── topics │ │ ├── cpp.txt │ │ ├── interviews.txt │ │ ├── css.txt │ │ ├── dsa.txt │ │ ├── go.txt │ │ ├── java.txt │ │ ├── podcasts.txt │ │ ├── dotnet.txt │ │ ├── react.txt │ │ ├── ml.txt │ │ ├── python.txt │ │ ├── php.txt │ │ ├── ruby.txt │ │ └── javascript.txt │ └── help.js ├── uptime │ └── uptime.js ├── eval │ ├── gentoken.js │ └── eval.js ├── server │ └── server.js ├── xkcd │ └── xkcd.js ├── pbf │ └── pbf.js ├── vote │ └── vote.js ├── weather │ └── weather.js ├── info │ └── info.js └── stream │ └── stream.js ├── tokens.json.example ├── .codeclimate.yml ├── .gitignore ├── .eslintrc.yaml ├── start.js ├── lib └── utils.js ├── test └── test.js ├── .travis.yml ├── settings.json ├── LICENSE ├── package.json ├── README.md └── TheAwesomeBot.js /.nvmrc: -------------------------------------------------------------------------------- 1 | node 2 | -------------------------------------------------------------------------------- /commands/quickref/references/linux.txt: -------------------------------------------------------------------------------- 1 | https://files.fosswire.com/2007/08/fwunixrefshot.png -------------------------------------------------------------------------------- /commands/quickref/references/regex.txt: -------------------------------------------------------------------------------- 1 | http://www.computerhope.com/jargon/r/regular-expression.gif -------------------------------------------------------------------------------- /commands/quickref/references/printf.txt: -------------------------------------------------------------------------------- 1 | https://codywu2010.files.wordpress.com/2014/10/printf_format.png -------------------------------------------------------------------------------- /commands/quickref/references/sqljoin.txt: -------------------------------------------------------------------------------- 1 | http://www.ilearnttoday.com/wp-content/uploads/2016/07/Visual_SQL_JOINS_orig.jpg -------------------------------------------------------------------------------- /tokens.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "discord": "", 3 | "google_geocode": "", 4 | "darksky": "", 5 | "replit": "" 6 | } 7 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | eslint: 4 | enabled: true 5 | channel: "eslint-3" 6 | fixme: 7 | enabled: true 8 | ratings: 9 | paths: 10 | - "**.js" 11 | -------------------------------------------------------------------------------- /commands/quickref/references/dotnet.txt: -------------------------------------------------------------------------------- 1 | http://www.hanselman.com/blog/content/binary/Windows-Live-Writer/ASP.NET-5-is-dead---Introducing-ASP.NE.0_E068/image_60140ec0-ce46-4dbf-a14f-4210eab7f42c.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /npm-debug.log 3 | 4 | *.swp 5 | *.swo 6 | .vscode 7 | 8 | *.DS_Store 9 | 10 | # ignore tokens file 11 | /tokens.json 12 | 13 | # tern server 14 | .tern-port 15 | -------------------------------------------------------------------------------- /commands/pro/proTerms.js: -------------------------------------------------------------------------------- 1 | const proTerms = require('fs') 2 | .readFileSync(`${__dirname}/proTerms.txt`, 'utf8') 3 | .split('\n') 4 | .map(str => str.split('|')); 5 | 6 | module.exports = proTerms; 7 | -------------------------------------------------------------------------------- /commands/quickref/references/style.txt: -------------------------------------------------------------------------------- 1 | Here is a link to Discord's text styles: 2 | 3 | https://support.discordapp.com/hc/en-us/articles/210298617-Markdown-Text-101-Chat-Formatting-Bold-Italic-Underline- 4 | -------------------------------------------------------------------------------- /commands/help/topics/cpp.txt: -------------------------------------------------------------------------------- 1 | See these links/courses for learning C++: 2 | 3 | Reference 4 | 5 | 6 | The Definitive C++ Book Guide and List 7 | 8 | 9 | C++ FAQ 10 | 11 | -------------------------------------------------------------------------------- /commands/uptime/uptime.js: -------------------------------------------------------------------------------- 1 | const time = require('../../lib/utils.js').time; 2 | 3 | module.exports = { 4 | usage: 'uptime - prints my uptime', 5 | run: (bot, message) => { 6 | message.channel.sendMessage( 7 | `Uptime: ${time.timeElapsed(bot.bootTime, new Date())}`); 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /commands/eval/gentoken.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | module.exports = (replitApiKey) => { 4 | const hmac = crypto.createHmac('sha256', replitApiKey); 5 | 6 | const timeCreated = Date.now(); 7 | hmac.update(timeCreated.toString()); 8 | const msgMac = hmac.digest('base64'); 9 | 10 | return { 11 | time_created: timeCreated, 12 | msg_mac: msgMac, 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | node: true 3 | rules: 4 | no-console: off 5 | max-len: [error, 120] 6 | prefer-template: off 7 | no-constant-condition: [error, {checkLoops: false}] 8 | no-unused-vars: [error, {argsIgnorePattern: '^(err$|_)'}] 9 | no-param-reassign: [error, {props: false}] 10 | import/no-unresolved: [error, {ignore: ['tokens.json']}] 11 | extends: airbnb 12 | installedESLint: true 13 | -------------------------------------------------------------------------------- /commands/quickref/references/codeblock.txt: -------------------------------------------------------------------------------- 1 | Use codeblocks for formatting code (line breaks matter!): 2 | \```language_name 3 | # code here 4 | \``` 5 | 6 | For example: 7 | \```python 8 | if True: 9 | print("Hi!") 10 | \``` 11 | prints: 12 | ```python 13 | if True: 14 | print("Hi!") 15 | ``` 16 | You can find the available language names here: 17 | -------------------------------------------------------------------------------- /commands/help/topics/interviews.txt: -------------------------------------------------------------------------------- 1 | Use these resources to prepare for interviews: 2 | 3 | Paid Book - Cracking the Coding Interview, 6th Edition 4 | 5 | 6 | Paid Book - Programming Interviews Exposed 7 | 8 | 9 | Paid Book - Elements of Programming Interviews !WARNING COMES IN SEVERAL LANGUAGES! 10 | 11 | -------------------------------------------------------------------------------- /start.js: -------------------------------------------------------------------------------- 1 | const Bot = require('./TheAwesomeBot.js'); 2 | const Tokens = require('./tokens.json'); 3 | 4 | function start() { 5 | // check for the discord token 6 | let jsonToken = false; 7 | if (Tokens) { 8 | jsonToken = Tokens.discord; 9 | } 10 | const token = jsonToken || process.env.DISCORD_TOKEN; 11 | if (!token) { 12 | throw Error('Discord token not set'); 13 | } 14 | 15 | (new Bot(token).init()); 16 | } 17 | 18 | start(); 19 | 20 | -------------------------------------------------------------------------------- /commands/help/topics/css.txt: -------------------------------------------------------------------------------- 1 | See these links for learning CSS: 2 | 3 | Tutorials/Documentation (In-depth) 4 | 5 | 6 | A Free Visual Guide to CSS: 7 | 8 | 9 | Free Course: Learn the basics of HTML and CSS 10 | 11 | 12 | Tutorials/Documentation (Good for Quick Reference) 13 | 14 | 15 | Tutorial: Simple introduction to CSS 16 | 17 | -------------------------------------------------------------------------------- /commands/help/topics/dsa.txt: -------------------------------------------------------------------------------- 1 | Use these resources to learn Data Structures and Algorithms: 2 | 3 | Free Course - Sedgewick's Coursera Part 1 4 | 5 | 6 | Free Course - Sedgewick's Coursera Part 2 7 | 8 | 9 | Paid Book - Sedgewick's Algorithms in Java 10 | 11 | 12 | Paid Book - CLRS/Introduction to Algorithms 13 | -------------------------------------------------------------------------------- /commands/help/topics/go.txt: -------------------------------------------------------------------------------- 1 | See these resources and links for learning Go: 2 | 3 | Documentation: 4 | 5 | 6 | Official Tour: 7 | 8 | 9 | Introductory Video: 10 | 11 | 12 | Godoc 13 | 14 | 15 | Effective Go <- READ THIS 16 | 17 | 18 | Sentdex - Introductory Go Videos 19 | -------------------------------------------------------------------------------- /commands/help/topics/java.txt: -------------------------------------------------------------------------------- 1 | See these links/courses for learning Java: 2 | 3 | Documentation (Java 8) 4 | 5 | 6 | Official Oracle tutorials 7 | 8 | 9 | Free Course: Object-Oriented programming with Java, part I & II (University of Helsinki) 10 | http://mooc.fi/courses/2013/programming-part-1/ 11 | 12 | Free materials: MIT 6.005: Software Construction (Warning: Needs some programming expirience) 13 | https://ocw.mit.edu/ans7870/6/6.005/s16/ 14 | -------------------------------------------------------------------------------- /commands/help/topics/podcasts.txt: -------------------------------------------------------------------------------- 1 | Here are some technology podcasts: 2 | 3 | *Coder Radio* by Jupiter Broadcasting 4 | 5 | 6 | *Talk Python to Me* by Michael Kennedy 7 | 8 | 9 | *Simple Programmer* by John Sonmez 10 | 11 | 12 | *Import This* by Kenneth Reitz 13 | 14 | 15 | *whiletruefm* by kennethlove, lethargilistic, samwho, and DiLemmA 16 | 17 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | const time = { 2 | MAGNITUDES: [ 3 | [1000 * 60 * 60, 'hours'], 4 | [1000 * 60, 'minutes'], 5 | [1000, 'seconds'], 6 | [1, 'milliseconds'], 7 | ], 8 | 9 | timeElapsed(before, after) { 10 | let diff = Math.abs(after - before); 11 | return this.MAGNITUDES.reduce((out, m) => { 12 | const current = Math.floor(diff / m[0]); 13 | diff %= m[0]; 14 | if (out.length || current) { 15 | out.push(`${current} ${m[1]}`); 16 | } 17 | return out; 18 | }, []).join(', '); 19 | }, 20 | }; 21 | 22 | module.exports = { 23 | time, 24 | }; 25 | -------------------------------------------------------------------------------- /commands/help/topics/dotnet.txt: -------------------------------------------------------------------------------- 1 | See these links/courses for learning .NET/C#: 2 | 3 | Free Course: Fundamentals for Absolute Beginners 4 | 5 | 6 | Video Series: Getting Started with Visual Studio & C# .NET with Nerdgasm 7 | 8 | 9 | Book: C# in Depth, Third Edition by Jon Skeet (http://meta.stackexchange.com/q/9134) 10 | 11 | 12 | Paid Course Library: Fundamentals, ASP.NET MVC, LINQ, Entity Framework and more 13 | 14 | -------------------------------------------------------------------------------- /commands/quickref/references/nohello.txt: -------------------------------------------------------------------------------- 1 | __**Please Don't Say Just Hello In Chat**__ 2 | ``` 3 | 2016-07-19 12:32:12 you: Hi 4 | 2016-07-19 12:32:15 co-worker: Hello. 5 | ## CO-WORKER WAITS WHILE YOU PHRASE YOUR QUESTION 6 | 2016-07-19 12:34:01 you: I'm working on [something] and I'm trying to do [etc...] 7 | 2016-07-19 12:35:21 co-worker: Oh, that's [answer...] 8 | ``` 9 | It's as if you called someone on the phone and said "Hi!" and then put them on hold! 10 | 11 | Please do this instead: 12 | ``` 13 | 2016-07-19 12:32:12 you: Hi -- I'm working on [something] and I'm trying to do [etc...] 14 | 2016-07-19 12:33:32 co-worker: [answers question] 15 | ``` 16 | *You can learn more at http://nohello.com/* 17 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape'); 2 | const TheAwesomeBot = require('../TheAwesomeBot'); 3 | 4 | const token = process.env.DISCORD_TOKEN || require('../tokens.json').discord; // eslint-disable-line global-require 5 | 6 | test('connect & disconnect', (t) => { 7 | t.timeoutAfter(15000); 8 | t.ok(token, 'discord token should be set'); 9 | 10 | const bot = new TheAwesomeBot(token); 11 | t.false(bot.isReady, 'bot should not be ready'); 12 | bot.init(); 13 | // wait for it to be ready 14 | const si = setInterval(() => { 15 | if (bot.isReady) { 16 | bot.deinit().then(() => { 17 | clearInterval(si); 18 | t.end(); 19 | }); 20 | } 21 | }, 5000); 22 | }); 23 | -------------------------------------------------------------------------------- /commands/server/server.js: -------------------------------------------------------------------------------- 1 | const discord = require('discord.js'); 2 | 3 | module.exports = { 4 | usage: 'server - prints info about the server', 5 | run: (bot, message) => { 6 | const embed = new discord.RichEmbed(); 7 | embed.setTitle('Server Owner') 8 | .setColor('#ff7260') 9 | .setAuthor(message.guild.name, message.guild.iconURL) 10 | .setDescription(message.guild.owner.user.username) 11 | .addField('Members', message.guild.members.size, true) 12 | .addField('Created', message.guild.createdAt.toString(), true) 13 | .addField('Emojis', 14 | message.guild.emojis.size > 0 ? message.guild.emojis.map(d => d.toString()).join(' ') : 'None'); 15 | message.channel.sendEmbed(embed); 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | dist: trusty 4 | script: 5 | - npm test 6 | - npm run lint . 7 | env: 8 | global: 9 | secure: SpEGhkGHrgZX5zB8o1926TeQDgO1t9npUm3cmTGGhV1GKpz9mt0WRqmIReOLVSG9j0UxF8RfqCHfVulGMg7C2YfOi7KecOF58IVWnajyh+zdhuf1dOEhb9Bl6YWWSzMJUUTVVNmeAf9AkhpcseKBsZLMlJeeK5eWgBkPqsiPvq1xT/8Y+QQ/pDZvmTPqUOB7OAghLZJGC1UuSy7R6uwk77JlbknSuJdb9lM2z4jIDxRrWjiUoaKUDFCJnER23whxvQOp4kp74YlmRC6oT2gQkez6mNlVvJkKDU7FM3QAanKlMjbx12k4++HpdiPfm8RLXUXVHC5CsD3TvhuCXtbsS2j8gpLI34NSC4AC5u/Xp9RgfSE96I0zY2upURFavZIDsTSCsu6XCvRjS0ltsZbVdVj8/WQxLW1YvfpgMQ5+NREBwLQpB/dil2lpoRyz5rSPYc6ta6nq9bNonpeHU7UaQnXIrKoKPdJOxJ+59sxjTwoNbjiS2uJmRfYSo6BCQrYHdx5p5eA92hBqjicjHEM9Px5+X4piRbCULnczV0HsPFATaCrzfW7YcxDgUIXhmm4lagPhaYo5LPT2PLwZ+pTL2en8h9Gbd31e2AyJfHT6WnTzxfk1PGnQPT2GBfhiDV05by8OaFToYPXIJWu6E4EiQc0HuEp4avSsZLYvvlf4Me4= 10 | -------------------------------------------------------------------------------- /commands/quickref/references/subnet.txt: -------------------------------------------------------------------------------- 1 | ``` 2 | Addresses Hosts Netmask Amount of a Class C 3 | /30 4 2 255.255.255.252 1/64 4 | /29 8 6 255.255.255.248 1/32 5 | /28 16 14 255.255.255.240 1/16 6 | /27 32 30 255.255.255.224 1/8 7 | /26 64 62 255.255.255.192 1/4 8 | /25 128 126 255.255.255.128 1/2 9 | /24 256 254 255.255.255.0 1 10 | /23 512 510 255.255.254.0 2 11 | /22 1024 1022 255.255.252.0 4 12 | /21 2048 2046 255.255.248.0 8 13 | /20 4096 4094 255.255.240.0 16 14 | /19 8192 8190 255.255.224.0 32 15 | /18 16384 16382 255.255.192.0 64 16 | /17 32768 32766 255.255.128.0 128 17 | /16 65536 65534 255.255.0.0 256 18 | ``` 19 | -------------------------------------------------------------------------------- /commands/help/topics/react.txt: -------------------------------------------------------------------------------- 1 | See these links for learning ReactJS/Flux/Redux: 2 | 3 | Hello World in React 4 | 5 | 6 | Free Course: Get Quickly in Pace with React.js Development 7 | 8 | 9 | Free Course: Essential Concepts 10 | 11 | 12 | Free Course: Continuation: Lifecycle Methods, Stateless Components and more 13 | 14 | 15 | Awesome react 16 | 17 | 18 | Redux doc's 19 | 20 | 21 | Dan Abramov's free course for redux 22 | 23 | 24 | React-redux-links 25 | 26 | -------------------------------------------------------------------------------- /commands/quickref/references/hotkeys.txt: -------------------------------------------------------------------------------- 1 | ``` 2 | CTRL - K Quick Switcher 3 | CTRL - ALT - UP ARROW Server Up 4 | CTRL - ALT - DOWN ARROW Server Down 5 | ALT - UP ARROW Channel Up 6 | ALT - DOWN ARROW Channel Down 7 | ALT - SHIFT - UP ARROW Unread channel up 8 | ALT - SHIFT - DOWN ARROW Unread channel down 9 | CTRL - SHIFT - ALT - UP ARROW Unread mention up 10 | CTRL - SHIFT - ALT - DOWN ARROW Unread mention down 11 | ESC Mark channel as read 12 | SHIFT - ESC Mark server as read 13 | CTRL- ALT - A Return to active audio channel 14 | CTRL - B Return to last text channel 15 | SHIFT - PGUP Jump to oldest unread message 16 | ``` 17 | -------------------------------------------------------------------------------- /commands/help/topics/ml.txt: -------------------------------------------------------------------------------- 1 | See these resources for tackling Machine Learning: 2 | 3 | Free Course - Andrew Ng's Course 4 | 5 | 6 | Free Course - Columbia's Artifical Intelligence Course 7 | 8 | 9 | Free Course - fast.ai's introductory course for ML 10 | 11 | 12 | Free Book - Introduction to Statistical Learning 13 | 14 | 15 | Free Book - Deep Learning Book 16 | 17 | 18 | Free Book - Neural Networks and Deep Learning 19 | 20 | 21 | YouTube - Sentdex 22 | 23 | 24 | YouTube - MIT OCW Artificial Intelligence 25 | 26 | 27 | -------------------------------------------------------------------------------- /commands/pro/proTerms.txt: -------------------------------------------------------------------------------- 1 | .NET|ASP.NET 2 | Ada 3 | Angular|Angular2 4 | AngularJS|Angular1 5 | Apache 6 | AppleScript 7 | Assembly|ASM|x86 8 | ATS 9 | AutoHotkey|AHK 10 | Bash 11 | Boo 12 | Bison 13 | C 14 | C-- 15 | C# 16 | C++ 17 | Caml 18 | Clojure 19 | Coq 20 | CoffeeScript 21 | CSS 22 | D|Dlang 23 | Dart 24 | Delphi 25 | Elm 26 | Eiffel 27 | Erlang 28 | Elixir 29 | F# 30 | Flex 31 | Groovy 32 | GLSL 33 | Go|Golang 34 | Haskell 35 | Haxe 36 | HLSL 37 | HTML 38 | Java 39 | JavaScript|JS 40 | Julia 41 | Kotlin 42 | LaTex 43 | Lua 44 | LOLCODE 45 | Lisp 46 | ML|SML 47 | Matlab 48 | Mongo 49 | Nim|Nimrod|Nimlang 50 | Ngnix 51 | OCaml 52 | Objective-C 53 | Oracle SQL 54 | Pascal 55 | Powershell 56 | Perl 57 | PHP 58 | Python 59 | R 60 | Redis 61 | Ruby 62 | Rust 63 | SmallTalk 64 | Squirrel 65 | Scheme 66 | Swift 67 | SQL 68 | Splunk 69 | TypeScript 70 | TeX 71 | VisualBasic 72 | Whitespace 73 | Wolfram 74 | -------------------------------------------------------------------------------- /commands/help/topics/python.txt: -------------------------------------------------------------------------------- 1 | See these links/courses for learning Python: 2 | 3 | Documentation 4 | 5 | 6 | Free to Read Online: A Byte of Python 7 | 8 | 9 | Free to Read Online: Introduction to Programming for Beginners 10 | 11 | 12 | Free to Read Online: Language Basics and Task Automation 13 | 14 | 15 | Paid Book (Free to Read Previous Versions): Tango With Django 16 | 17 | 18 | Free Course: Introduction to Computing using Python 19 | 20 | 21 | Paid Course Library: In-depth Fundamentals, Flask, Django and more 22 | 23 | 24 | Free Course: Problem Solving with Algorithms and Data Structures using Python 25 | 26 | -------------------------------------------------------------------------------- /settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "bot_cmd": "!bot", 3 | "voting": { 4 | "immuneRoles": ["Admins", "Mods"], 5 | "voteThreshold": 5, 6 | "timeout_in_minutes": 5 7 | }, 8 | "xkcd": { 9 | "timeLimit": 60, 10 | "limitMessages": false 11 | }, 12 | "weather": { 13 | "icons": { 14 | "clear-day": ":sunny:", 15 | "clear-night": ":crescent_moon:", 16 | "rain": ":cloud_rain:", 17 | "snow": ":cloud_snow:", 18 | "sleet": ":cloud_snow:", 19 | "partly-cloudy-day": ":partly_sunny:", 20 | "partly-cloudy-night": ":partly_sunny:", 21 | "fog": ":fog:", 22 | "cloudy":":cloud:", 23 | "wind": ":wind_blowing_face:" 24 | } 25 | }, 26 | "info":{ 27 | "repo":"rgoliveira/awesomebot" 28 | }, 29 | "commands": [ 30 | "help", 31 | "eval", 32 | "pro", 33 | "stream", 34 | "uptime", 35 | "vote", 36 | "xkcd", 37 | "weather", 38 | "server", 39 | "pbf", 40 | "quickref", 41 | "info" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /commands/help/help.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const getFileList = (dirName) => { 5 | const fileList = fs.readdirSync(dirName); 6 | return fileList || []; 7 | }; 8 | 9 | const loadHelpText = (filename) => { 10 | const content = fs.readFileSync(filename, 'utf8'); 11 | return content || ''; 12 | }; 13 | 14 | // basic help commands 15 | const knownTopics = {}; 16 | 17 | module.exports = { 18 | usage: [ 19 | 'help - links to the new resource list at https://github.com/progdisc/resources', 20 | ], 21 | 22 | run: (bot, message) => { 23 | let r = 'This command is deprecated.\n'; 24 | r += 'See https://github.com/progdisc/resources for our new and improved resource list.'; 25 | message.channel.sendMessage(r); 26 | }, 27 | 28 | init: () => { 29 | console.log('Loading help topics...'); 30 | getFileList(path.join(__dirname, 'topics')).forEach((fn) => { 31 | knownTopics[path.basename(fn, '.txt')] = loadHelpText(path.join(__dirname, 'topics', fn)); 32 | }); 33 | }, 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /commands/help/topics/php.txt: -------------------------------------------------------------------------------- 1 | See these links/courses for learning PHP: 2 | 3 | PHP Official Documentation (select a language on the page) 4 | 5 | 6 | Learn PHP the right way (online free book) 7 | 8 | 9 | PHP 5 Tutorials 10 | 11 | 12 | A simple tutorial (good introduction) 13 | 14 | 15 | Free Course: Fundamental Programming Concepts 16 | 17 | 18 | Codeschool's PHP courses 19 | 20 | 21 | Beginner PHP at Treehouse 22 | 23 | 24 | Intermediate PHP at Treehouse 25 | 26 | 27 | PHP 5: Interactive tutorial 28 | 29 | 30 | 31 | See these resources for learning what's new in PHP 7: 32 | 33 | PHP 7: Up and running 34 | 35 | 36 | PHP 7: Interactive tutorial 37 | 38 | -------------------------------------------------------------------------------- /commands/help/topics/ruby.txt: -------------------------------------------------------------------------------- 1 | See these links/courses for learning Ruby: 2 | 3 | Official Ruby Core 2.4.1 Documentation 4 | 5 | 6 | Quickstart Guide: Ruby In Twenty Minutes 7 | 8 | 9 | Cheatsheet/Reference: 10 | 11 | 12 | Learn to Program with Ruby 13 | 14 | 15 | Free (Language Transition): Ruby From Other Languages 16 | 17 | 18 | Free (Exercise Focused): Learn Ruby the Hard Way 19 | 20 | 21 | Free (Exercise Focused): Codecademy Ruby 22 | 23 | 24 | Free (Ruby on Rails 5) : Ruby on Rails Tutorial 25 | 26 | 27 | Free (Ruby on Rails): Learn Ruby on Rails from Scratch 28 | 29 | 30 | Paid (Course Library): TeamTreehouse: Learn Ruby 31 | 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 All contributors of AwesomeBot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TheAwesomeBot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "TheAwesomeBot.js", 6 | "scripts": { 7 | "start": "node start.js", 8 | "test": "if [ \"$TRAVIS_PULL_REQUEST\" = \"false\" ]; then node test/*.js | tap-summary --no-progress; fi", 9 | "lint": "eslint" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/rgoliveira/AwesomeBot.git" 14 | }, 15 | "author": "", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/rgoliveira/AwesomeBot/issues" 19 | }, 20 | "homepage": "https://github.com/rgoliveira/AwesomeBot", 21 | "dependencies": { 22 | "cheerio": "^0.22.0", 23 | "discord.js": "^11.0.0", 24 | "replit-client": "^0.19.0", 25 | "request": "^2.81.0", 26 | "request-promise": "^4.2.0", 27 | "xmlhttprequest": "^1.8.0" 28 | }, 29 | "devDependencies": { 30 | "eslint": "^3.0.1", 31 | "eslint-config-airbnb": "^14.1.0", 32 | "eslint-plugin-import": "^2.2.0", 33 | "eslint-plugin-jsx-a11y": "^4.0.0", 34 | "eslint-plugin-react": "^6.9.0", 35 | "tap-summary": "^3.0.1", 36 | "tape": "^4.6.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /commands/help/topics/javascript.txt: -------------------------------------------------------------------------------- 1 | See these links/courses for learning JavaScript: 2 | 3 | Tutorials/Documentation 4 | 5 | 6 | Free to Read Online: A Modern Introduction to Programming 7 | 8 | 9 | Free to Read Online: Exploring ES6 10 | 11 | 12 | Free to Read Online: Exploring ES2016-ES2017 13 | 14 | 15 | Free Course: Fundamental Programming Concepts 16 | 17 | 18 | Free Course Library: Learn to Code and Help Nonprofits 19 | 20 | 21 | Free Course: Basic JavaScript (~3 weeks) 22 | 23 | 24 | Talk: Four Layers of JavaScript OOP (1:09:02) 25 | 26 | 27 | Talk: How Node.js' Event Loop Works (26:52) 28 | 29 | 30 | Paid Course Library: In-depth Fundamentals, Node, React, jQuery and more 31 | 32 | 33 | Free to Read Online Series: You Don't Know JS (Core Mechanisms, Advanced) 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AwesomeBot [![Build Status](https://travis-ci.org/progdisc/AwesomeBot.svg?branch=master)](https://travis-ci.org/progdisc/AwesomeBot) [![Code Climate](https://codeclimate.com/github/progdisc/AwesomeBot/badges/gpa.svg)](https://codeclimate.com/github/progdisc/AwesomeBot) [![david-dm](https://david-dm.org/progdisc/AwesomeBot.svg)](https://david-dm.org/progdisc/AwesomeBot) 2 | 3 | ## How to run it 4 | First, make sure you have the latest version of [Node.js](https://nodejs.org/) and [npm](https://github.com/npm/npm). 5 | We recommend using [nvm](https://github.com/creationix/nvm) to manage these, and there's a `.nvmrc` file in this project, so just run this and you're all set: 6 | ```sh 7 | nvm use 8 | ``` 9 | 10 | Now you need to install the dependencies: 11 | ```sh 12 | npm install 13 | ``` 14 | 15 | Then, in order to log your bot into Discord, set [your bot token](https://discordapp.com/developers/applications/me): 16 | ```sh 17 | export DISCORD_TOKEN= 18 | # you could, instead, fill it in the ./settings.json file 19 | ``` 20 | 21 | And finally, start your bot with: 22 | ```sh 23 | npm start 24 | ``` 25 | 26 | Before pushing any changes or submitting PRs, don't forget to run eslint, or our CI may reject your request: 27 | ```sh 28 | npm run lint -- . 29 | ``` 30 | -------------------------------------------------------------------------------- /commands/quickref/quickref.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const getFileList = (dirName) => { 5 | const fileList = fs.readdirSync(dirName); 6 | return fileList || []; 7 | }; 8 | 9 | const loadReferences = (filename) => { 10 | const content = fs.readFileSync(filename, 'utf8'); 11 | return content || ''; 12 | }; 13 | 14 | const references = {}; 15 | 16 | module.exports = { 17 | usage: [ 18 | 'quickref - displays quick reference for ', 19 | 'quickref - list known references', 20 | ], 21 | run: (bot, message, cmdArgs) => { 22 | if (cmdArgs) { 23 | const response = references[cmdArgs.toLowerCase()]; 24 | 25 | if (response) { 26 | message.channel.sendMessage( 27 | `${response}`); 28 | } else { 29 | message.channel.sendMessage('I don\'t have any references for that. If you have a suggestion, let us know!'); 30 | } 31 | } else { 32 | let r = '\nreferences I have ready to go:'; 33 | r += '\n```'; 34 | r += Object.keys(references).map(t => `\n - ${t}`).join(''); 35 | r += '\n```'; 36 | message.channel.sendMessage(r); 37 | } 38 | }, 39 | init: () => { 40 | console.log('Loading quickrefs...'); 41 | getFileList(path.join(__dirname, 'references')).forEach((fn) => { 42 | references[path.basename(fn, '.txt')] = loadReferences(path.join(__dirname, 'references', fn)); 43 | }); 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /commands/xkcd/xkcd.js: -------------------------------------------------------------------------------- 1 | const request = require('request-promise'); 2 | const cheerio = require('cheerio'); 3 | 4 | const ddgHeaders = { 5 | 'accept-language': 'en-US,en;q=0.8', 6 | 'upgrade-insecure-requests': 1, 7 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' + 8 | 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36', 9 | }; 10 | 11 | const ddgUrlTemplate = 'https://duckduckgo.com/html/?q=$q xkcd'; 12 | 13 | function parseXkcdDataFromXkcdUrl(xkcdUrl) { 14 | return request(xkcdUrl).then((xkcdBody) => { 15 | const xkcdData = JSON.parse(xkcdBody); 16 | 17 | if (xkcdData) { 18 | return '```diff\n' + 19 | `Title: ${xkcdData.safe_title}\n` + 20 | `Alt Text: ${xkcdData.alt}\n` + 21 | '```\n' + 22 | `${xkcdData.img}`; 23 | } 24 | // eslint-disable-next-line no-throw-literal 25 | throw 'I\'m sorry, there was a problem retrieving a XKCD.'; 26 | }); 27 | } 28 | 29 | function parseXkcdUrlFromDuckDuckGo(ddgBody) { 30 | const ddgParsed = cheerio.load(ddgBody); 31 | let xkcdUrl = false; 32 | 33 | try { 34 | ddgParsed('.result__a').each((i, link) => { 35 | const href = link.attribs.href; 36 | 37 | if (href.search(/^https?:\/\/(www\.)?xkcd\.com\/\d+/) !== -1 && xkcdUrl === false) { 38 | xkcdUrl = href + 'info.0.json'; 39 | } 40 | }); 41 | } catch (err) { 42 | // eslint-disable-next-line no-throw-literal 43 | throw 'There was a problem with DuckDuckGo query.'; 44 | } 45 | 46 | if (!xkcdUrl) { 47 | // eslint-disable-next-line no-throw-literal 48 | throw 'I\'m sorry, I couldn\'t find a xkcd.'; 49 | } else { 50 | return xkcdUrl; 51 | } 52 | } 53 | 54 | function findXkcdFromKeywords(keywords) { 55 | const ddgUrl = ddgUrlTemplate.replace('$q', encodeURI(keywords)); 56 | 57 | return request({ 58 | url: ddgUrl, 59 | headers: ddgHeaders, 60 | }).then(parseXkcdUrlFromDuckDuckGo) 61 | .then(parseXkcdDataFromXkcdUrl); 62 | } 63 | 64 | module.exports = { 65 | usage: 'xkcd - finds a xkcd comic with relevant keywords', 66 | run: (bot, message, cmdArgs) => { 67 | if (!cmdArgs) { 68 | return true; 69 | } 70 | 71 | findXkcdFromKeywords(cmdArgs).then((data) => { 72 | message.channel.sendMessage(data); 73 | }) 74 | .catch((err) => { 75 | message.channel.sendMessage(err); 76 | }); 77 | 78 | return false; 79 | }, 80 | }; 81 | -------------------------------------------------------------------------------- /commands/pbf/pbf.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const cheerio = require('cheerio'); 3 | 4 | function parsePbfLink(pbfLink, message) { 5 | request(pbfLink, (error, response, body) => { 6 | if (!error && response.statusCode === 200) { 7 | // we have successfully got a response 8 | const htmlBody = cheerio.load(body); 9 | 10 | if (htmlBody('#topimg')) { 11 | const img = htmlBody('#topimg'); 12 | message.channel.sendMessage('```diff\n' + 13 | `Title: ${img.attr('alt')}\n` + 14 | '```\n' + 15 | `http://pbfcomics.com${img.attr('src')}`); 16 | } else { 17 | message.channel.sendMessage(`I'm sorry ${message.author}, i couldn't find a PBF Comic.`); 18 | } 19 | } 20 | }); 21 | } 22 | 23 | module.exports = { 24 | usage: 'pbf - finds a pbf comic with relevant keywords. Random keyword selects random comic.', 25 | run: (bot, message, cmdArgs) => { 26 | let pbfLink = false; 27 | 28 | if (cmdArgs === 'random') { 29 | pbfLink = 'http://pbfcomics.com/random'; 30 | parsePbfLink(pbfLink, message); 31 | } else { 32 | const options = { 33 | url: `https://duckduckgo.com/html/?q=${cmdArgs}%20pbfcomics`, 34 | headers: { 35 | 'accept-language': 'en-US,en;q=0.8', 36 | 'upgrade-insecure-requests': 1, 37 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' + 38 | 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36', 39 | }, 40 | }; 41 | request(options, (err, res, bod) => { 42 | const pbfBody = cheerio.load(bod); 43 | try { 44 | pbfBody('.result__a').each((i, link) => { 45 | const href = link.attribs.href; 46 | if (href.search(/^https?:\/\/(www\.)?pbfcomics\.com\/\d+\//) !== -1 && pbfLink === false) { 47 | pbfLink = href; 48 | } 49 | }); 50 | } catch (e) { 51 | message.channel.sendMessage('There was a problem with DuckDuckGo query.'); 52 | } 53 | // we are done with finding a link 54 | if (!pbfLink) { 55 | // link is either empty (this should NOT happen) or we don't have a link 56 | message.channel.sendMessage(`I'm sorry ${message.author}, i couldn't find a PBF Comic.`); 57 | } else { 58 | parsePbfLink(pbfLink, message); 59 | } 60 | }); 61 | } 62 | return false; 63 | }, 64 | 65 | }; 66 | -------------------------------------------------------------------------------- /commands/vote/vote.js: -------------------------------------------------------------------------------- 1 | function handleKick(bot, member, _guild) { 2 | member.kick().catch((err) => { 3 | if (err) console.log(err); 4 | }); 5 | } 6 | 7 | function handleMute(bot, member, _guild) { 8 | member.setMute(true).catch((err) => { 9 | if (err) console.log(err); 10 | }); 11 | } 12 | 13 | const voteTypes = { 14 | kick: handleKick, 15 | mute: handleMute, 16 | }; 17 | 18 | /* 19 | * currentVotes: dictionary of votes 20 | * { 21 | * : { 22 | * : { 23 | * username: , 24 | * votes: [, 26 | * } 27 | * } 28 | * } 29 | */ 30 | const currentVotes = {}; 31 | Object.keys(voteTypes).forEach((k) => { 32 | currentVotes[k] = {}; 33 | }); 34 | 35 | function setIntersection(setA, setB) { 36 | return new Set([...setA].filter(x => setB.has(x))); 37 | } 38 | 39 | function processVote(type, bot, message, guild, member) { 40 | let voting = currentVotes[type][member.user.username]; 41 | 42 | if (!voting) { 43 | // sets a timeout for this voting 44 | const timeoutClj = () => { 45 | message.channel.sendMessage(`Vote to ${type} ${member} has timed out. Phew!`); 46 | delete currentVotes[type][member.user.username]; 47 | }; 48 | const timeoutObj = setTimeout(timeoutClj, bot.settings.voting.timeout_in_minutes * 1000 * 60); 49 | 50 | voting = { 51 | username: member.user.username, 52 | votes: [], 53 | timeout: timeoutObj, 54 | }; 55 | currentVotes[type][member.user.username] = voting; 56 | } 57 | 58 | // ignore votes by the same user 59 | if (voting.votes.indexOf(message.author.username) >= 0) { 60 | return; 61 | } 62 | voting.votes.push(message.author.username); 63 | if (voting.votes.length >= bot.settings.voting.voteThreshold) { 64 | clearTimeout(voting.timeout); 65 | message.channel.sendMessage(`Sorry, ${member}, but their wish is my command!`); 66 | voteTypes[type](bot, member, guild); 67 | delete currentVotes[type][member.user.username]; 68 | } else { 69 | let msg = `[${voting.votes.length}/${bot.settings.voting.voteThreshold}]`; 70 | msg += ` votes to ${type} ${member}!`; 71 | message.channel.sendMessage(msg); 72 | } 73 | } 74 | 75 | module.exports = { 76 | usage: `vote <${Object.keys(voteTypes).join('|')}> <@user> - start a vote against <@user>`, 77 | 78 | run: (bot, message, cmdArgs) => { 79 | // command validation 80 | const voteRe = new RegExp(`^(${Object.keys(voteTypes).join('|')})`, 'i'); 81 | const reMatch = cmdArgs.match(voteRe); 82 | if (!reMatch) return true; 83 | 84 | const guild = message.channel.guild; 85 | const voteType = reMatch[1]; 86 | 87 | const user = message.mentions.users.first(); 88 | if (!user) { 89 | message.channel.sendMessage('You need to specify a valid member!'); 90 | return false; 91 | } 92 | const member = guild.members.get(user.id); 93 | 94 | // user validation 95 | // warning: assume bot is in one guild only 96 | if (user === message.author) { 97 | message.channel.sendMessage('You can\'t start a vote against yourself, silly.'); 98 | return false; 99 | } else if (user === bot.client.user) { 100 | message.channel.sendMessage(`I'm sorry ${message.author}, I'm afraid I can't let you do that.,`); 101 | return false; 102 | } 103 | 104 | // roles validation 105 | const userRoles = new Set(member.roles.array().map(r => r.name)); 106 | if (setIntersection(userRoles, new Set(bot.settings.voting.immuneRoles)).size > 0) { 107 | message.channel.sendMessage('try.is(\'nice\') === true'); 108 | return false; 109 | } 110 | 111 | processVote(voteType, bot, message, guild, member); 112 | return false; 113 | }, 114 | }; 115 | 116 | -------------------------------------------------------------------------------- /commands/pro/pro.js: -------------------------------------------------------------------------------- 1 | function fixEscapes(str) { 2 | return str.replace(/[^a-z0-9|]/ig, '\\$&'); 3 | } 4 | 5 | let proLangRe; 6 | let pros; 7 | const proHelpText = {}; 8 | 9 | function updateProsMatcher() { 10 | /* eslint global-require: off */ 11 | delete require.cache[require.resolve('./proTerms.js')]; 12 | pros = {}; 13 | 14 | const terms = require('./proTerms.js') 15 | .filter(termList => termList[0].length > 0) 16 | .map((termList) => { 17 | const termPros = new Set(); 18 | termPros.original = termList[0]; 19 | 20 | termList.forEach((term) => { 21 | pros[term.toLowerCase()] = termPros; 22 | }); 23 | 24 | return fixEscapes(termList.join('|')); 25 | }); 26 | 27 | proLangRe = new RegExp(`(?:^|\\W)(${terms.join('|')})(?:$|\\W)`, 'gi'); 28 | } 29 | 30 | 31 | function getProsOnline(guild) { 32 | return new Set(guild.members 33 | .filter(m => m.roles.find('name', 'Helpers') && ['online', 'idle'].includes(m.presence.status)) 34 | .map(p => p.user.username)); 35 | } 36 | 37 | 38 | function loadAndMatchPros(bot) { 39 | updateProsMatcher(); 40 | const helpChannel = bot.client.channels.find('name', 'help-directory'); 41 | 42 | return helpChannel.fetchMessages({ limit: 100 }) 43 | .then((messages) => { 44 | messages.forEach((messageObj) => { 45 | proHelpText[messageObj.author.id] = messageObj.content; 46 | proLangRe.lastIndex = 0; 47 | while (true) { 48 | const match = proLangRe.exec(messageObj.content); 49 | if (!match) { 50 | break; 51 | } 52 | pros[match[1].toLowerCase()].add(messageObj.author.username); 53 | } 54 | }); 55 | }); 56 | } 57 | 58 | 59 | function getPros(bot, lang) { 60 | if (!pros[lang]) { 61 | return null; 62 | } 63 | const langPros = Array.from(pros[lang]); 64 | const guild = bot.client.guilds.first(); 65 | const online = getProsOnline(guild); 66 | return langPros.filter(user => online.has(user)).join('\n'); 67 | } 68 | 69 | module.exports = { 70 | usage: [ 71 | 'pro - list of people who knows about ', 72 | 'pro - get help directory entry for specific pro', 73 | 'pro reset - reload all the pro data (mod only)', 74 | ], 75 | 76 | run(bot, message, cmdArgs) { 77 | if (!cmdArgs) { 78 | return true; 79 | } 80 | 81 | if (message.mentions.users.size > 0) { 82 | const memberId = message.mentions.users.first().id; 83 | if (memberId in proHelpText) { 84 | let response = `**Help Directory entry for user ${message.mentions.users.first().username}:**\n`; 85 | response += proHelpText[memberId]; 86 | message.channel.sendMessage(response); 87 | } else { 88 | message.channel.sendMessage(`Could not find user ${message.mentions.users.first().username} in directory`); 89 | } 90 | return false; 91 | } 92 | 93 | let lang = cmdArgs.toLowerCase().trim(); 94 | 95 | if (lang === 'reset' && bot.isAdminOrMod(message.member)) { 96 | loadAndMatchPros(bot).then(() => { 97 | message.channel.sendMessage('Pros list refreshed.'); 98 | return false; 99 | }) 100 | .catch((err) => { 101 | console.error(err); 102 | console.error(err.stack); 103 | }); 104 | return false; 105 | } 106 | 107 | proLangRe.lastIndex = 0; 108 | const match = proLangRe.exec(lang); 109 | lang = ((match && match[1]) || lang).toLowerCase(); 110 | 111 | const foundPros = getPros(bot, lang); 112 | message.channel.sendMessage(foundPros ? 113 | `Here are some pros online that can help with **${pros[lang].original}**: \n${foundPros}` : 114 | `No pros found for ${cmdArgs} :(`); 115 | return false; 116 | }, 117 | 118 | init(bot) { 119 | console.log('Loading pros...'); 120 | loadAndMatchPros(bot) 121 | .then(() => { 122 | console.log('Done reading in pros from #helpdirectory!'); 123 | }) 124 | .catch((err) => { 125 | console.error(err); 126 | console.error(err.stack); 127 | }); 128 | }, 129 | }; 130 | 131 | -------------------------------------------------------------------------------- /commands/eval/eval.js: -------------------------------------------------------------------------------- 1 | const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; 2 | const ReplitClient = require('replit-client'); 3 | const gentoken = require('./gentoken'); 4 | 5 | global.XMLHttpRequest = XMLHttpRequest; 6 | 7 | // list of langs: 8 | // https://github.com/replit/ReplitClient.js#replitclienthostname-port-language-token 9 | const availableLanguages = [ 10 | 'c', 11 | 'cpp', 12 | 'cpp11', 13 | 'csharp', 14 | 'fsharp', 15 | 'go', 16 | 'java', 17 | 'lua', 18 | 'nodejs', 19 | 'php', 20 | 'python', 21 | 'python3', 22 | 'ruby', 23 | 'rust', 24 | 'swift', 25 | ]; 26 | 27 | // map usual lang names to replit names 28 | const langAliases = { 29 | 'c#': 'csharp', 30 | 'c++': 'cpp', 31 | 'c++11': 'cpp11', 32 | 'f#': 'fsharp', 33 | js: 'nodejs', 34 | javascript: 'nodejs', 35 | py2: 'python', 36 | py: 'python3', 37 | py3: 'python3', 38 | rb: 'ruby', 39 | }; 40 | 41 | const validateLang = (lang) => { 42 | if (typeof lang !== 'string') { 43 | return false; 44 | } 45 | 46 | const l = langAliases[lang.toLowerCase()] || lang.toLowerCase(); 47 | 48 | if (availableLanguages.includes(l)) { 49 | return l; 50 | } 51 | return false; 52 | }; 53 | 54 | module.exports = { 55 | usage: [ 56 | 'eval - run code in repl.it', 57 | 'eval - show available languages', 58 | ], 59 | 60 | run: (bot, message, cmdArgs) => { 61 | if (!cmdArgs) { 62 | const langList = availableLanguages.reduce((prev, x) => { 63 | let langEntry = `${prev}${prev ? '\n' : ''}- ${x}`; 64 | const aliases = Object.keys(langAliases) 65 | .filter(alias => langAliases[alias] === x) 66 | .join(', '); 67 | if (aliases) { 68 | langEntry += ` (${aliases})`; 69 | } 70 | return langEntry; 71 | }, ''); 72 | message.reply(`available languages:\n\`\`\`\n${langList}\n\`\`\`\n`); 73 | return; 74 | } 75 | 76 | // parsing the messages against regex to obtain the lang and code 77 | let rxLangs = Object.keys(langAliases) 78 | .map((v) => { 79 | if (v === 'c++') { 80 | return 'c\\+\\+'; 81 | } else if (v === 'c++11') { 82 | return 'c\\+\\+11'; 83 | } 84 | return v; 85 | }); 86 | rxLangs.push(...availableLanguages); 87 | rxLangs = rxLangs.sort((a, b) => b.length - a.length).join('|'); 88 | const rx = new RegExp('^(`{0,3})(' + rxLangs + ')\\s*((.|\\s)+)(\\1)$', 'gi'); 89 | const argsArr = rx.exec(cmdArgs); 90 | let lang = argsArr[2].toLowerCase(); 91 | const code = (new RegExp('^(`{0,3})((' + rxLangs + '|.{0})\\s)?\\s*((.|\\s)+)(\\1)$', 'gi')).exec(argsArr[3])[4]; 92 | lang = validateLang(lang); 93 | if (!lang) { 94 | message.reply('Sorry, I don\'t know that language!'); 95 | return; 96 | } 97 | 98 | const apiToken = bot.settings.tokens.replit || process.env.REPLIT_TOKEN; 99 | const repl = new ReplitClient( 100 | 'api.repl.it', 101 | 80, 102 | lang, gentoken(apiToken)); 103 | 104 | message.channel.sendMessage('⏲ evaluating...') 105 | .then((evalMsg) => { 106 | let newContent = ''; 107 | repl.evaluateOnce( 108 | code, { 109 | stdout: (output) => { 110 | newContent += `Code output:\n\`\`\`\n${output}\n\`\`\`\n`; 111 | }, 112 | }).then( 113 | (result) => { 114 | if (result.error) { 115 | newContent += `Error:\n\`\`\`${result.error}\`\`\`\n`; 116 | } else { 117 | newContent += `Result:\n\`\`\`${result.data}\`\`\`\n`; 118 | } 119 | evalMsg.edit(newContent); 120 | }, 121 | (error) => { 122 | newContent += `Error connecting to repl.it!\`\`\`${error}\`\`\`\n`; 123 | evalMsg.edit(newContent); 124 | console.error(error); 125 | }); 126 | }) 127 | .catch(console.error); 128 | }, 129 | }; 130 | 131 | -------------------------------------------------------------------------------- /commands/weather/weather.js: -------------------------------------------------------------------------------- 1 | const request = require('request-promise'); 2 | const discord = require('discord.js'); 3 | 4 | let weatherConfig; 5 | let tokens; 6 | const geocodeEndpoint = 'https://maps.googleapis.com/maps/api/geocode/json?key=gkey&address=input'; 7 | const darkskyEndpoint = 'https://api.darksky.net/forecast/key/lat,lng'; 8 | 9 | function fahrenheitToCelcius(degree) { 10 | return (((degree - 32) * 5) / 9).toFixed(0); 11 | } 12 | 13 | function getGeocodeData(address) { 14 | let requestURL = geocodeEndpoint 15 | .replace('gkey', tokens.google_geocode) 16 | .replace('input', address); 17 | requestURL = encodeURI(requestURL); 18 | 19 | return request(requestURL).then((body) => { 20 | const geocodeData = JSON.parse(body); 21 | 22 | if (geocodeData.status !== 'OK') { 23 | // eslint-disable-next-line no-throw-literal 24 | throw 'I\'m sorry, the address couldn\'t be detected.'; 25 | } 26 | 27 | const result = { 28 | address: geocodeData.results[0].formatted_address, 29 | coordinate: geocodeData.results[0].geometry.location, 30 | }; 31 | 32 | return result; 33 | }); 34 | } 35 | 36 | function getWeatherData(location) { 37 | const requestURL = darkskyEndpoint 38 | .replace('key', tokens.darksky) 39 | .replace('lat', location.coordinate.lat) 40 | .replace('lng', location.coordinate.lng); 41 | 42 | return request(requestURL).then((body) => { 43 | const weatherData = JSON.parse(body); 44 | 45 | const offset = weatherData.offset; 46 | const utcTime = weatherData.currently.time; 47 | // datetime is weird in javascript, please do change this part if you can 48 | const localTime = new Date(utcTime * 1000); 49 | localTime.setHours(localTime.getHours() + offset); 50 | let dateString; 51 | // toGMTString prints out timezone of host so we slice it off 52 | if (offset > 0) { 53 | dateString = localTime.toUTCString() + ' +' + offset; 54 | } else { 55 | dateString = localTime.toUTCString() + ' ' + offset; 56 | } 57 | 58 | const temperatureF = weatherData.currently.temperature.toFixed(0); 59 | const temperatureC = fahrenheitToCelcius(temperatureF); 60 | const summary = weatherData.currently.summary; 61 | const humidity = (weatherData.currently.humidity * 100).toFixed(0); 62 | // convert speed to freedom units 63 | const windSpeed = (weatherData.currently.windSpeed * 1.61).toFixed(0); 64 | const pressure = weatherData.currently.pressure.toFixed(0); 65 | const icon = weatherData.currently.icon; 66 | const timezone = weatherData.timezone; 67 | 68 | const result = { 69 | location: location.address, 70 | // A temporary fix until the eventual refactor. 71 | address: location.address, 72 | offset, 73 | localTime, 74 | dateString, 75 | temperatureC, 76 | temperatureF, 77 | summary, 78 | humidity, 79 | windSpeed, 80 | pressure, 81 | icon, 82 | timezone, 83 | }; 84 | 85 | return result; 86 | }); 87 | } 88 | 89 | function generateEmbed(data, verbose) { 90 | const embed = new discord.RichEmbed(); 91 | 92 | if (verbose) { 93 | embed.setColor('#4286f4') 94 | .setFooter(`Local Time: ${data.dateString}`) 95 | .setTitle(`Weather in ${data.location}`) 96 | .addField('Summary', data.summary) 97 | .addField('Temperature °C', `${data.temperatureC} °C`, true) 98 | .addField('Temperature °F', `${data.temperatureF} °F`, true) 99 | .addField('Timezone', data.timezone, true) 100 | .addField('Humidity', `${data.humidity}%`, true) 101 | .addField('Wind Speed', `${data.windSpeed} km/h`, true) 102 | .addField('Air Pressure', `${data.pressure} mbar`, true) 103 | .setDescription(weatherConfig.icons[data.icon]); 104 | } else { 105 | embed.setColor('#4286f4') 106 | .setFooter(`Local Time: ${data.dateString}`) 107 | .setTitle(`Weather in ${data.address}`) 108 | .addField('Summary', data.summary) 109 | .addField('Temperature', `${data.temperatureC} °C / ${data.temperatureF} °F`, true) 110 | .addField('Humidity', `${data.humidity}%`, true) 111 | .setDescription(weatherConfig.icons[data.icon]); 112 | } 113 | 114 | return embed; 115 | } 116 | 117 | module.exports = { 118 | usage: [ 119 | 'weather
- brings current weather info for given address', 120 | 'weather -v
- brings additional weather info for given address', 121 | ], 122 | run: (bot, message, cmdArgs) => { 123 | if (!cmdArgs) return true; 124 | 125 | let args = cmdArgs.split(' '); 126 | const verbose = args[0] === '-v'; 127 | args = verbose ? args.slice(1) : args; 128 | 129 | getGeocodeData(args).then(getWeatherData) 130 | .then(data => generateEmbed(data, verbose)) 131 | .then(embed => message.channel.sendEmbed(embed)) 132 | .catch(err => message.channel.sendMessage(err.toString())); 133 | return false; 134 | }, 135 | init: (bot) => { 136 | weatherConfig = bot.settings.weather; 137 | tokens = bot.settings.tokens; 138 | }, 139 | }; 140 | -------------------------------------------------------------------------------- /commands/info/info.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const exec = require('child_process').exec; 3 | const discord = require('discord.js'); 4 | 5 | const time = require('../../lib/utils.js').time; 6 | 7 | const githubCommits = 'https://api.github.com/repos/$repo/commits'; 8 | const githubContributors = 'https://api.github.com/repos/$repo/contributors'; 9 | const githubRepo = 'https://github.com/$repo'; 10 | const commitTemplate = '$username - $message'; 11 | const markdownLink = '[$text]($link)'; 12 | const description = 'Message cmd for available commands.'; 13 | let config; 14 | const lastCommit = {}; 15 | const currentCommit = {}; 16 | let contributorsMessage = ''; 17 | const githubHeaders = { 18 | 'User-Agent': 'TheAwesomeBot', 19 | 'Accept': 'application/vnd.github.v3+json', // eslint-disable-line quote-props 20 | }; 21 | 22 | function getLastCommit() { 23 | request({ 24 | url: githubCommits.replace('$repo', config.repo), 25 | headers: githubHeaders, 26 | }, (err, response, body) => { 27 | if (err || response.statusCode !== 200) { 28 | lastCommit.link = 'https://github.com/404'; 29 | lastCommit.message = 'Couldn\'t retrieve commit data.'; 30 | return; 31 | } 32 | const commitData = JSON.parse(body); 33 | // TODO (sam): Instead of depending on nullness of the variable, 34 | // act according to github api docs 35 | if (commitData[0] == null) { 36 | lastCommit.message = 'Couldn\'t retrieve commit data.'; 37 | return; 38 | } 39 | const commitMessage = commitData[0].commit.message.replace('\n\n', ' ') 40 | .replace('\n', ' '); 41 | lastCommit.link = commitData[0].html_url; 42 | lastCommit.username = commitData[0].author ? commitData[0].author.login : 'unkown'; 43 | lastCommit.message = commitTemplate.replace('$username', lastCommit.username) 44 | .replace('$message', commitMessage); 45 | }); 46 | } 47 | 48 | function getCurrentCommit() { 49 | exec('git show --oneline -s', (err, stdout) => { 50 | const gitOutput = stdout.replace('\n\n', '').replace('\n', ''); 51 | currentCommit.shortSHA = gitOutput.split(' ')[0]; 52 | 53 | request({ 54 | url: githubCommits.replace('$repo', config.repo) + '/' + currentCommit.shortSHA, 55 | headers: githubHeaders, 56 | }, (error, response, body) => { 57 | if (error || response.statusCode !== 200) { 58 | currentCommit.link = 'https://github.com/404'; 59 | return; 60 | } 61 | currentCommit.link = JSON.parse(body).html_url; 62 | currentCommit.username = JSON.parse(body).author ? JSON.parse(body).author.login : 'unkown'; 63 | currentCommit.message = commitTemplate.replace('$username', currentCommit.username) 64 | .replace('$message', gitOutput.split(' ').slice(1).join(' ')); 65 | }); 66 | }); 67 | } 68 | 69 | function getContributors() { 70 | request({ 71 | url: githubContributors.replace('$repo', config.repo), 72 | headers: githubHeaders, 73 | }, (error, response, body) => { 74 | let jsonData = JSON.parse(body); 75 | if (Array.isArray(jsonData) && jsonData.length >= 10) { 76 | jsonData = jsonData.slice(0, 10); 77 | contributorsMessage = jsonData.slice(0, 10).reduce((acc, cv) => 78 | acc + markdownLink.replace('$text', cv.login).replace('$link', cv.html_url) + '\n' 79 | , ''); 80 | } else { 81 | contributorsMessage = 'There was an error trying to get the contributors list :('; 82 | } 83 | }); 84 | } 85 | 86 | function infoInit(bot) { 87 | config = bot.settings.info; 88 | // get current checked out commit from git 89 | getCurrentCommit(); 90 | // get latest commit in git repo 91 | getLastCommit(); 92 | getContributors(); 93 | } 94 | 95 | function infoRun(bot, message, cmdArgs) { 96 | if (cmdArgs) { 97 | if (cmdArgs === 'contributors') { 98 | const contributorsEmbed = new discord.RichEmbed(); 99 | contributorsEmbed.setColor('#4286f4') 100 | .setTitle('Top 10 contributors of AwesomeBot:') 101 | .setDescription(contributorsMessage); 102 | message.channel.sendEmbed(contributorsEmbed); 103 | } 104 | } else { 105 | const embed = new discord.RichEmbed(); 106 | embed.setColor('#4286f4') 107 | .setAuthor('TheAwesomeBot', bot.client.user.avatarURL, githubRepo.replace('$repo', config.repo)) 108 | .setFooter(description.replace('cmd', bot.settings.bot_cmd)) 109 | .addField('Uptime', time.timeElapsed(bot.bootTime, new Date())) 110 | .addField('Latest Commit', markdownLink.replace('$text', lastCommit.message).replace('$link', lastCommit.link)) 111 | .setDescription('An open source bot made with :heart:'); 112 | // let the users know if bot is working on a rolled back commit 113 | if (currentCommit !== lastCommit) { 114 | embed.addField('Current Commit', markdownLink.replace('$text', currentCommit.message) 115 | .replace('$link', currentCommit.link)); 116 | } 117 | message.channel.sendEmbed(embed); 118 | } 119 | } 120 | 121 | module.exports = { 122 | usage: [ 123 | 'info - displays information about bot', 124 | 'info contributors - displays contributors', 125 | ], 126 | init: infoInit, 127 | run: infoRun, 128 | }; 129 | -------------------------------------------------------------------------------- /TheAwesomeBot.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | const path = require('path'); 3 | const Discord = require('discord.js'); 4 | 5 | const Settings = require(path.join(__dirname, 'settings.json')); // eslint-disable-line import/no-dynamic-require 6 | let Tokens; 7 | try { 8 | // eslint-disable-next-line global-require, import/no-dynamic-require 9 | Tokens = require(path.join(__dirname, 'tokens.json')); 10 | } catch (e) { 11 | Tokens = {}; 12 | } 13 | 14 | class TheAwesomeBot { 15 | constructor(token, discordOpt) { 16 | this.bootTime = new Date(); 17 | this.token = token; 18 | this.client = new Discord.Client(discordOpt || { autoReconnect: true }); 19 | this.settings = Settings; 20 | this.settings.tokens = Tokens; // insert tokens into our settings obj 21 | this.commands = {}; 22 | this.usageList = ''; 23 | 24 | // store the RE as they're expensive to create 25 | this.cmd_re = new RegExp(`^${this.settings.bot_cmd}\\s+([^\\s]+)\\s*([^]*)\\s*`, 'i'); 26 | 27 | // flags if connected and client is ready 28 | this.isReady = false; 29 | } 30 | 31 | onMessage() { 32 | return (message) => { 33 | // don't respond to own messages 34 | if (this.client.user.username === message.author.username) { 35 | return; 36 | } 37 | 38 | // check if message is a command 39 | const cmdMatch = message.cleanContent.match(this.cmd_re); 40 | 41 | // not a known command 42 | if (!cmdMatch || Object.keys(this.commands).indexOf(cmdMatch[1]) === -1) { 43 | if (message.content.match(new RegExp(`^${this.settings.bot_cmd}[\\s]*( .*)?$`, 'i'))) { 44 | let helpText = 'maybe try these valid commands? *kthnxbye!*\n\n```'; 45 | helpText += this.usageList; 46 | helpText += '```'; 47 | message.channel.sendMessage(helpText); 48 | } 49 | return; 50 | } 51 | 52 | // process commands 53 | const cmd = cmdMatch[1]; 54 | const cmdArgs = cmdMatch[2].trim(); 55 | 56 | let showUsage; 57 | 58 | try { 59 | showUsage = this.commands[cmd].run(this, message, cmdArgs); 60 | } catch (err) { 61 | message.channel.sendMessage('There was an error running the command:\n' + 62 | '```\n' + err.toString() + '\n```'); 63 | console.error(err); 64 | console.error(err.stack); 65 | } 66 | 67 | if (showUsage === true) { 68 | let usage = this.commands[cmd].usage; 69 | if (typeof usage !== 'string') { 70 | usage = usage.join('\n'); 71 | } 72 | message.channel.sendMessage('```\n' + usage + '\n```'); 73 | } 74 | }; 75 | } 76 | 77 | onReady() { 78 | return (() => { 79 | console.log('\nConnected to discord server!'); 80 | console.log('Running initializations...'); 81 | Object.keys(this.commands).filter(cmd => 82 | typeof this.commands[cmd].init === 'function') 83 | .forEach(cmd => this.commands[cmd].init(this)); 84 | this.isReady = true; 85 | }); 86 | } 87 | 88 | serverNewMember() { 89 | return ((server, user) => this.client.sendMessage(user, this.usageList)); 90 | } 91 | 92 | onDisconnected() { 93 | return () => 94 | console.warn('Bot has been disconnected from server...'); 95 | } 96 | 97 | onError() { 98 | return ((err) => { 99 | console.error('error: ', err); 100 | console.error(err.trace); 101 | }); 102 | } 103 | 104 | loadCommands(cmdList) { 105 | this.usageList = ''; 106 | cmdList.forEach((cmd) => { 107 | const fullpath = path.join(__dirname, 'commands', cmd, `${cmd}.js`); 108 | const script = require(fullpath); // eslint-disable-line global-require, import/no-dynamic-require 109 | this.commands[cmd] = script; 110 | 111 | const usageObj = script.usage; 112 | if (usageObj) { 113 | const usageStrs = []; 114 | if (Array.isArray(usageObj)) { 115 | usageObj.forEach(u => usageStrs.push(u)); 116 | } else { 117 | usageStrs.push(usageObj.toString()); 118 | } 119 | 120 | this.usageList += usageStrs.reduce((list, str) => list + `\n- ${this.settings.bot_cmd} ${str}`, ''); 121 | } 122 | }); 123 | } 124 | 125 | init() { 126 | // load commands 127 | console.log('Loading commands...'); 128 | this.loadCommands(this.settings.commands); 129 | 130 | // setup events 131 | console.log('Setting up event bindings...'); 132 | this.client 133 | .on('ready', this.onReady()) 134 | .on('serverNewMember', this.serverNewMember()) 135 | .on('message', this.onMessage()) 136 | .on('error', this.onError()); 137 | 138 | console.log('Connecting...'); 139 | // return the promise from "login()" 140 | return this.client.login(this.token); 141 | } 142 | 143 | deinit() { 144 | // disconnect gracefully 145 | this.isReady = false; 146 | // return the promise from "destroy()" 147 | return this.client.destroy(); 148 | } 149 | 150 | isAdminOrMod(member) { 151 | const immuneRoles = new Set(this.settings.voting.immuneRoles); 152 | const userRoles = new Set(member.roles.array().map(r => r.name)); 153 | const setIntersection = [...userRoles].filter(r => immuneRoles.has(r)); 154 | return setIntersection.length > 0; 155 | } 156 | } 157 | 158 | module.exports = TheAwesomeBot; 159 | 160 | -------------------------------------------------------------------------------- /commands/stream/stream.js: -------------------------------------------------------------------------------- 1 | /* 2 | This module is meant to handle the command '!bot streams #channel' 3 | 4 | Notes: 5 | - streamCommands is an array with the valid stream commands for the keys in channels.js: 6 | e.g ['!bot streams python', '!bot streams php'...] 7 | e.g ['!bot streams remove [channel] [user]'] 8 | 9 | How it works: 10 | - streams is an object with the following schema: 11 | 12 | streams: { 13 | [channel] : { 14 | [user]: link , 15 | [user]: link , 16 | . 17 | . 18 | . 19 | } 20 | } 21 | - streams is an in-memory-object of sorts to keep track of streams. 22 | */ 23 | 24 | // in memory object containing join.me stream 25 | const streams = {}; 26 | 27 | function putStreamInObject(topic, user, link, description) { 28 | if (!streams[topic]) { 29 | streams[topic] = {}; 30 | } 31 | 32 | streams[topic][user] = { link, description, channel: '' }; 33 | 34 | // console.log(`Put in Object: Key: ${topic}, User Key: ${user}`); 35 | } 36 | 37 | function createChannel(title, bot, message, topic, user) { 38 | return message.guild.createChannel(title, 'text').then((channel) => { 39 | streams[topic][user].channel = channel; 40 | return channel; 41 | }); 42 | } 43 | 44 | function setTopicToLink(channel, link, bot, topic, user) { 45 | return channel.setTopic(link).then(() => { 46 | streams[topic][user].channel = channel; 47 | return channel; 48 | }); 49 | } 50 | 51 | 52 | function deleteStreamInObject(topic, user) { 53 | if (Object.keys(streams[topic]).length === 1) { 54 | delete streams[topic]; 55 | } else { 56 | delete streams[topic][user]; 57 | } 58 | } 59 | 60 | 61 | const commands = { 62 | create: function handleCreateStream(bot, message, args) { 63 | let [, topic, link, user] = args.split(' '); // eslint-disable-line prefer-const 64 | 65 | if (!topic || !link) { 66 | return message.channel.sendMessage('err, please provide topic and link!'); 67 | } 68 | 69 | if (link.indexOf('http://') === -1 && link.indexOf('https://') === -1) { 70 | return message.channel.sendMessage('a valid link must be supplied (starting with http/https)!'); 71 | } 72 | 73 | user = message.mentions.users.first(); 74 | if (user) { 75 | // Creating a channel for someone else 76 | // The keys in the topics object are username mention id 77 | } else { 78 | // Creating a channel for you 79 | // The keys in the topics object are username mention id 80 | user = message.author; 81 | } 82 | const channelFormat = `stream_${user.username}_${topic}`; 83 | 84 | const defaultDescription = 85 | `${user} is streaming about ${topic}`; 86 | 87 | putStreamInObject(topic, user.id, link, defaultDescription); 88 | 89 | const existingChannel = bot.client.channels.get('name', channelFormat); 90 | 91 | if (existingChannel) { 92 | streams[topic][user.id].channel = existingChannel; 93 | streams[topic][user.id].link = link; 94 | 95 | existingChannel.setTopic(link).catch(err => 96 | existingChannel.sendMessage('There was an error setting the existings channel topic!')); 97 | 98 | return message.channel.sendMessage('Channel already exists.. Updated stream link!'); 99 | } 100 | return createChannel(channelFormat, bot, message, topic, user.id) 101 | .then(createdChannel => setTopicToLink(createdChannel, link, bot, topic, user.id)) 102 | .then(channelWithTopic => message.channel.sendMessage(`Created ${channelWithTopic}!`)) 103 | .catch(err => message.channel.sendMessage(`Sorry, could not create channel (${err})`)); 104 | }, 105 | 106 | remove: function handleRemoveStream(bot, message) { 107 | const user = message.mentions.users.first(); 108 | 109 | const topics = Object.keys(streams); 110 | const id = user ? user.id : message.author.id; 111 | 112 | if (!bot.isAdminOrMod(message.member) && id !== message.author.id) { 113 | message.channel.sendMessage('Only admins or mods can remove others\' streams.'); 114 | return; 115 | } 116 | 117 | topics.forEach((topic) => { 118 | if (streams[topic][id]) { 119 | const channelToDelete = streams[topic][id].channel; 120 | 121 | deleteStreamInObject(topic, id); 122 | 123 | channelToDelete.delete().catch(err => 124 | message.channel.sendMessage('Sorry, could not delete channel')); 125 | 126 | message.channel.sendMessage( 127 | `Removed ${user || message.author} from active streamers list and deleted #${channelToDelete.name}`); 128 | } else { 129 | // user has no stream in this topic 130 | // return message.channel.sendMessage(`Could not find ${user}`); 131 | } 132 | }); 133 | }, 134 | 135 | list: function listStreams(bot, message) { 136 | let buildMessage = 'Available streams: \n'; 137 | 138 | const topics = Object.keys(streams); 139 | 140 | if (topics.length === 0) { 141 | return message.channel.sendMessage('No streams! :frowning:'); 142 | } 143 | 144 | topics.forEach((topic) => { 145 | buildMessage += `**\n${topic}**\n`; 146 | Object.keys(streams[topic]).forEach((stream, index) => { 147 | const link = streams[topic][stream].link; 148 | const description = streams[topic][stream].description; 149 | 150 | buildMessage += ` ${index + 1}. ${link} - ${description}\n`; 151 | }); 152 | }); 153 | 154 | return message.channel.sendMessage(buildMessage); 155 | }, 156 | 157 | removeall: function removeAllStreams(bot, message) { 158 | if (message && !bot.isAdminOrMod(message.member)) { 159 | message.channel.sendMessage('Only Admins or Mods can delete all stream channels'); 160 | return; 161 | } 162 | 163 | console.log('Removing all stream channels..'); 164 | bot.client.guilds.first().channels.filter(channel => 165 | channel && channel.name !== undefined && channel.name.startsWith('stream')) 166 | .forEach(channel => channel.delete()); 167 | 168 | Object.keys(streams).forEach(topic => delete streams[topic]); 169 | }, 170 | }; 171 | 172 | module.exports = { 173 | usage: [ 174 | 'stream create [@user] - creates stream about [by @user]', 175 | 'stream list - displays streamings about ', 176 | 'stream remove <@user> - removes a streaming by <@user>', 177 | 'stream removeall - removes every streaming (Mods only)', 178 | ], 179 | 180 | run: (bot, message, cmdArgs) => { 181 | const cmdFn = commands[cmdArgs.split(' ')[0]]; 182 | if (!cmdFn) return true; 183 | cmdFn(bot, message, cmdArgs); 184 | return false; 185 | }, 186 | 187 | init: (bot) => { 188 | commands.removeall(bot); 189 | }, 190 | }; 191 | 192 | --------------------------------------------------------------------------------