├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── commands ├── anime.js ├── base.js ├── emote.js ├── eval.js ├── eyes.js ├── flip.js ├── getcommand.js ├── gifspeed.js ├── google.js ├── mal.js ├── manga.js ├── members.js ├── np.js ├── ping.js ├── playing.js ├── purge.js ├── react.js ├── reboot.js ├── regional.js ├── reload.js ├── s.js ├── shinobu.js ├── smug.js ├── stats.js ├── status.js ├── tag.js ├── tl.js ├── uptime.js └── user.js ├── config.sample.json ├── kuro.js └── package.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 2017, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | "experimentalObjectRestSpread": true 7 | } 8 | }, 9 | "env": { 10 | "es6": true, 11 | "node": true 12 | }, 13 | "extends": "eslint:recommended", 14 | "rules": { 15 | "no-console": "off", 16 | "no-extra-parens": ["warn", "all", { "nestedBinaryExpressions": false }], 17 | "accessor-pairs": "warn", 18 | "array-callback-return": "error", 19 | "complexity": "warn", 20 | "consistent-return": "error", 21 | "curly": ["error", "multi-line", "consistent"], 22 | "dot-location": ["error", "property"], 23 | "dot-notation": "error", 24 | "eqeqeq": "error", 25 | "no-empty-function": "error", 26 | "no-floating-decimal": "error", 27 | "no-implied-eval": "error", 28 | "no-invalid-this": "error", 29 | "no-lone-blocks": "error", 30 | "no-multi-spaces": "error", 31 | "no-new-func": "error", 32 | "no-new-wrappers": "error", 33 | "no-new": "error", 34 | "no-octal-escape": "error", 35 | "no-return-assign": "error", 36 | "no-self-compare": "error", 37 | "no-sequences": "error", 38 | "no-throw-literal": "error", 39 | "no-unmodified-loop-condition": "error", 40 | "no-unused-expressions": "error", 41 | "no-useless-call": "error", 42 | "no-useless-concat": "error", 43 | "no-useless-escape": "error", 44 | "no-void": "error", 45 | "no-warning-comments": "warn", 46 | "wrap-iife": "error", 47 | "yoda": "error", 48 | "no-label-var": "error", 49 | "no-shadow": "error", 50 | "no-undef-init": "error", 51 | "handle-callback-err": "error", 52 | "no-mixed-requires": "error", 53 | "no-new-require": "error", 54 | "no-path-concat": "error", 55 | "array-bracket-spacing": "error", 56 | "block-spacing": "error", 57 | "brace-style": ["error", "1tbs", { "allowSingleLine": true }], 58 | "camelcase": "error", 59 | "comma-dangle": "error", 60 | "comma-spacing": "error", 61 | "comma-style": "error", 62 | "computed-property-spacing": "error", 63 | "consistent-this": "error", 64 | "eol-last": "error", 65 | "func-style": ["error", "declaration", { "allowArrowFunctions": true }], 66 | "id-length": ["error", { "exceptions": ["i", "j", "a", "b", "_"] }], 67 | "indent": ["error", "tab", { "SwitchCase": 1 }], 68 | "key-spacing": "error", 69 | "keyword-spacing": ["error", { 70 | "overrides": { 71 | "if": { "after": true }, 72 | "for": { "after": true }, 73 | "while": { "after": true }, 74 | "catch": { "after": true }, 75 | "switch": { "after": true } 76 | } 77 | }], 78 | "max-depth": "error", 79 | "max-len": ["error", 120, 2], 80 | "max-nested-callbacks": ["error", { "max": 4 }], 81 | "max-statements-per-line": ["error", { "max": 2 }], 82 | "new-cap": "error", 83 | "newline-per-chained-call": ["error", { "ignoreChainWithDepth": 3 }], 84 | "no-array-constructor": "error", 85 | "no-bitwise": "warn", 86 | "no-inline-comments": "error", 87 | "no-lonely-if": "error", 88 | "no-mixed-operators": "error", 89 | "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], 90 | "no-new-object": "error", 91 | "no-spaced-func": "error", 92 | "no-trailing-spaces": "error", 93 | "no-unneeded-ternary": "error", 94 | "no-whitespace-before-property": "error", 95 | "object-curly-newline": "error", 96 | "object-curly-spacing": ["error", "always"], 97 | "operator-assignment": "error", 98 | "operator-linebreak": ["error", "before"], 99 | "padded-blocks": ["error", "never"], 100 | "quote-props": ["error", "as-needed"], 101 | "quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }], 102 | "semi-spacing": "error", 103 | "semi": "off", 104 | "space-before-blocks": "error", 105 | "space-before-function-paren": ["error", "never"], 106 | "space-in-parens": "error", 107 | "space-infix-ops": "error", 108 | "space-unary-ops": "error", 109 | "spaced-comment": "error", 110 | "unicode-bom": "error", 111 | "arrow-spacing": "error", 112 | "no-duplicate-imports": "error", 113 | "no-useless-computed-key": "error", 114 | "no-useless-constructor": "error", 115 | "prefer-arrow-callback": "error", 116 | "prefer-rest-params": "error", 117 | "prefer-spread": "error", 118 | "prefer-template": "error", 119 | "rest-spread-spacing": "error", 120 | "template-curly-spacing": "error", 121 | "yield-star-spacing": "error" 122 | } 123 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | files/ 4 | stickers/ 5 | .eslintrc.json 6 | config.js 7 | config.json 8 | db 9 | npm-debug.log 10 | 11 | # Commands people shouldn't have :araragi: 12 | commands/radio.js 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Pitu 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Kuro Bot](http://i.imgur.com/ohS1PwH.png) 2 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/kanadeko/Kuro/master/LICENSE) 3 | [![Chat / Support](https://img.shields.io/badge/Chat%20%2F%20Support-discord-7289DA.svg?style=flat-square)](https://discord.gg/5g6vgwn) 4 | 5 | Kuro is an easy to use self bot that is shifting more and more into a framework while preserving its ease of use. It sits on top of [discord.js](https://github.com/hydrabolt/discord.js/). 6 | NodeJS version 6+ is ***REQUIRED***. [Installing Node.js](https://nodejs.org/en/download/package-manager/) 7 | 8 | [> Check this video to see how it works!](https://my.mixtape.moe/pwcrem.webm) 9 | 10 | ### v4.1.0 Important Notes: 11 | This only applies if you are upgrading from a previous version. 12 | In order to make the chat less jumpy and contribute with data caps and mobile users, sticker now behave differently than they did before. Stickers get uploaded to [safe.moe](https://safe.moe) now so they can be used inside an embed. By using embeds, kuro can now edit your message to display the sticker instead of deleting it and sending a new one. Even though that embeds are ugly this makes it so everyone can see your sticker faster by allowing caching of the URL on Discord's side. 13 | 14 | So if you are upgrading to a new version, please run `!s migrate` to migrate your stickers to the new embed system. 15 | 16 | ## Installing: 17 | 1. Ensure you have node installed 18 | 2. Run `npm i -g kuro-cli` 19 | 3. Run `kuro-cli` 20 | 4. Follow the instructions on screen 21 | 22 | [kuro-cli](https://github.com/Pitu/kuro-cli) is a utility that will install and manage your Kuro installation. Whenever there's a new version available you can run `kuro-cli` and it will download, upgrade, update dependencies and restart automatically. Some options are not covered on the cli just yet, so you can go ahead and open `config.json` to look and modify them if you want. 23 | 24 | If you don't want to use `kuro-cli`, simply clone the repo, `npm install`, rename the `config.sample.json` to `config.json` and modify it's values with your data. 25 | 26 | ## Modules: 27 | This new update brings every command in the form of separate modules. Inside each module you can make up the stuff you want, and you can execute it by calling the module name without the extension. There's a sample module ready for you to duplicate called `base.js`. 28 | 29 | Example of a simple module with no dependencies that returns the server's member count on which you send the command: 30 | ```javascript 31 | exports.run = function(msg, args) { 32 | msg.delete() 33 | msg.channel.send('', { 34 | 'embed': { 35 | 'title': msg.guild.name, 36 | 'description': `Member Count: ${msg.guild.memberCount}`, 37 | 'color': 15473237 38 | } 39 | }) 40 | } 41 | ``` 42 | 43 | Pretty easy stuff. 44 | If you want me to include a module you've made, send a PR with your stuff and I'll look at it. 45 | 46 | ## Bundled modules 47 | 48 | Each module has detailed instructions inside their own files. Take a look at them for further details on how to use. 49 | 50 | - `anime ` 51 | Shows the first occurence of the searched anime on kitsu.io and returns a summary of it 52 | 53 | - `emote [emote]` 54 | Shows information about a custom emote. 55 | 56 | - `eval [expression]` 57 | A module to eval expressions. Dangerous stuff, don't use unless pretty sure of what you're doing. 58 | 59 | - `eyes` 60 | A module that edits a message to add the effect of animated eyes. You probably should update the emoji name if you're not on Pilar's server. 61 | 62 | - `flip ` 63 | sllɐqǝzɐɯɐ sı sıɥʇ 64 | 65 | - `getcommand [module]` 66 | Sends the specified module's source to the chat. Ex: `!getcommand base` would print `base.js` contents to chat. 67 | 68 | - `gifspeed [url]` 69 | Removes delay between frames of the given gif url and uploads it. 70 | 71 | - `google ` 72 | Creates a link to Google with your query when someone asks stupid questions 73 | 74 | - `mal` 75 | Prints information about your MyAnimeList username. 76 | 77 | - `manga ` 78 | Shows the first occurence of the searched manga on kitsu.io and returns a summary of it 79 | 80 | - `members` 81 | Shows the server's member count. 82 | 83 | - `ping` 84 | Simple tool to check delay between your bot and Discord. 85 | 86 | - `playing [message]` 87 | Change your `playing` status on Discord to the specified string. (Note you wont be able to see it due to a Discord limitation). 88 | 89 | - `purge [number of messages]` 90 | Grabs the supplied amount of messages from chat and deletes those that are yours. 91 | 92 | - `reboot` 93 | Reboots the Kuro. (Only works if using pm2|forever). 94 | 95 | - `reload` 96 | Reloads all the modules (Useful when developing). 97 | 98 | - `react [message]` 99 | React to the last message with regional characters. a-z 0-9, no spaces. 100 | 101 | - `regional [message]` 102 | Sends a message using regional character emojis. 103 | 104 | - `s [name] | [add|del|ren]` 105 | A module to manage stickers like Telegram does. Upload a sticker with a given name, and then make kuro paste it when you trigger the command. 106 | 107 | - `smug` 108 | Displays smug looking anime girls with patronizing looks on their faces. 109 | 110 | - `stats` 111 | Displays an embed with statistics. 112 | 113 | - `status [online|idle|dnd|offline]` 114 | The status you want to appear as whenever you're offline, since using Kuro will make discord think you're always online. 115 | 116 | - `tag [name] | [add|del|ren]` 117 | Saves the given text into a tag for later usage. For example `tag add kuro https://github.com/kanadeko/Kuro` would print `https://github.com/kanadeko/Kuro` every time I do `tag kuro` 118 | 119 | - `tl` 120 | Tries to translate the last message to english. 121 | 122 | - `uptime` 123 | Displays how long the bot has been running. 124 | 125 | - `user <@user>` 126 | Displays information about the tagged user 127 | -------------------------------------------------------------------------------- /commands/anime.js: -------------------------------------------------------------------------------- 1 | const Kitsu = require('kitsu.js') 2 | const kitsu = new Kitsu() 3 | let kuro 4 | 5 | exports.init = function(bot) { kuro = bot } 6 | 7 | exports.run = function(msg, args) { 8 | let search = args.toString().replace(/,/g, ' ') 9 | kitsu.searchAnime(search) 10 | .then(result => { 11 | if (result.length === 0) { 12 | return msg.edit(`No results found for: **${search}**`) 13 | } 14 | return prepareEmbed(msg, result[0]) 15 | }) 16 | .catch(err => { 17 | console.error(err) 18 | return msg.edit('There was an error processing the search, please check the console') 19 | }); 20 | } 21 | 22 | function prepareEmbed(msg, item) { 23 | const { slug, synopsis, titles, averageRating, posterImage, episodeCount, showType } = item 24 | const url = `https://kitsu.io/anime/${slug}` 25 | 26 | msg.edit('', { 27 | embed: { 28 | type: 'rich', 29 | title: titles.romaji, 30 | url, 31 | description: `**Synopsis:**\n${synopsis.substring(0, 450)}...`, 32 | color: kuro.config.embedColor, 33 | fields: [ 34 | { 35 | name: '❯ Type', 36 | value: fixCase(showType), 37 | inline: true 38 | }, 39 | { 40 | name: '❯ Episodes', 41 | value: episodeCount, 42 | inline: true 43 | }, 44 | { 45 | name: '❯ Rating', 46 | value: averageRating, 47 | inline: true 48 | } 49 | ], 50 | author: { 51 | name: 'kitsu.io', 52 | url: 'https://kitsu.io' 53 | }, 54 | thumbnail: { url: posterImage.small } 55 | } 56 | }) 57 | } 58 | 59 | function fixCase(str) { 60 | return str.toLowerCase().replace(/(^| )(\w)/g, s => s.toUpperCase()) 61 | } 62 | -------------------------------------------------------------------------------- /commands/base.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg, args) { 5 | // Code goes here 6 | } 7 | -------------------------------------------------------------------------------- /commands/emote.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg, args) { 5 | if (args[0] === undefined) return msg.delete() 6 | if (!args[0].startsWith('<:')) return kuro.edit(msg, 'Not a valid emote') 7 | 8 | let id = args[0].substring(args[0].lastIndexOf(':') + 1, args[0].lastIndexOf('>')) 9 | let emoteInfo = kuro.emojis.get(id) 10 | if (!emoteInfo) return kuro.edit(msg, 'No emote with that id') 11 | 12 | return msg.edit({ 13 | embed: { 14 | type: 'rich', 15 | color: 15473237, 16 | thumbnail: { url: `https://cdn.discordapp.com/emojis/${emoteInfo.id}.png` }, 17 | fields: [ 18 | { 19 | name: 'Emote Name', 20 | value: emoteInfo.name, 21 | inline: true 22 | }, 23 | { 24 | name: 'Emote ID', 25 | value: `[${emoteInfo.id}](https://cdn.discordapp.com/emojis/${emoteInfo.id}.png)`, 26 | inline: true 27 | }, 28 | { 29 | name: 'Managed', 30 | value: emoteInfo.managed, 31 | inline: true 32 | }, 33 | { 34 | name: 'Server', 35 | value: `${emoteInfo.guild.name}`, 36 | inline: true 37 | } 38 | ] 39 | } 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /commands/eval.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg, args) { 5 | var code = args.join(' ') 6 | 7 | try { 8 | var evaled = eval(code) 9 | if (typeof evaled !== 'string') { 10 | evaled = require('util').inspect(evaled, { depth: 0 }) 11 | } 12 | msg.edit(msg.content + '\n```js\n' + clean(evaled) + '\n```') 13 | } catch (err) { 14 | msg.edit(msg.content + '\n```css\nERROR:\n' + clean(err) + '\n```') 15 | kuro.error(clean(err)) 16 | } 17 | } 18 | 19 | function clean(text) { 20 | if (typeof (text) === 'string') { 21 | return text.replace(/`/g, '`' + String.fromCharCode(8203)).replace(/@/g, '@' + String.fromCharCode(8203)) 22 | } else { 23 | return text 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /commands/eyes.js: -------------------------------------------------------------------------------- 1 | exports.run = function(msg) { 2 | msg.delete() 3 | msg.channel.send('👀').then(msg => { 4 | setTimeout(() => { 5 | msg.edit('<:eyes2:248874616142036992>').then(msg => { 6 | setTimeout(() => { 7 | msg.edit('👀').then(msg => { 8 | setTimeout(() => { 9 | msg.edit('<:eyes2:248874616142036992>').then(msg => { 10 | setTimeout(() => { 11 | msg.edit('👀').then(msg => { 12 | setTimeout(() => { 13 | msg.edit('<:eyes2:248874616142036992>').then(msg => { 14 | setTimeout(() => { 15 | msg.edit('👀').then(msg => { 16 | setTimeout(() => { 17 | msg.edit('<:eyes2:248874616142036992>') 18 | }, 500) 19 | }) 20 | }, 500) 21 | }) 22 | }, 500) 23 | }) 24 | }, 500) 25 | }) 26 | }, 500) 27 | }) 28 | }, 500) 29 | }) 30 | }, 500) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /commands/flip.js: -------------------------------------------------------------------------------- 1 | const flipTable = { 2 | a: '\u0250', 3 | b: 'q', 4 | c: '\u0254', 5 | d: 'p', 6 | e: '\u01DD', 7 | f: '\u025F', 8 | g: '\u0183', 9 | h: '\u0265', 10 | i: '\u0131', 11 | j: '\u027E', 12 | k: '\u029E', 13 | //l : '\u0283', 14 | m: '\u026F', 15 | n: 'u', 16 | r: '\u0279', 17 | t: '\u0287', 18 | v: '\u028C', 19 | w: '\u028D', 20 | y: '\u028E', 21 | '.': '\u02D9', 22 | '[': ']', 23 | '(': ')', 24 | '{': '}', 25 | '?': '\u00BF', 26 | '!': '\u00A1', 27 | "\'": ',', 28 | '<': '>', 29 | '_': '\u203E', 30 | ';': '\u061B', 31 | '\u203F': '\u2040', 32 | '\u2045': '\u2046', 33 | '\u2234': '\u2235', 34 | '\r': '\n' 35 | } 36 | 37 | exports.run = function(msg, args) { 38 | return msg.edit(flipString(args.toString().replace(/,/g, ' '))) 39 | } 40 | 41 | function flipString(aString) { 42 | let last = aString.length - 1; 43 | let result = new Array(aString.length) 44 | for (let i = last; i >= 0; --i) { 45 | let c = aString.charAt(i) 46 | let r = flipTable[c] 47 | result[last - i] = r !== undefined ? r : c 48 | } 49 | return result.join('') 50 | } 51 | -------------------------------------------------------------------------------- /commands/getcommand.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg, args) { 5 | if (args.length === 0) return kuro.edit(msg, 'You need to provide a file', 1000) 6 | 7 | try { 8 | require('fs').readFile(`./commands/${args}.js`, 'utf-8', (err, data) => { 9 | if (err) return kuro.error(err) 10 | return msg.edit(`**__Overview of ${args}.js__**\n\`\`\`javascript\n${data}\n\`\`\``) 11 | }) 12 | } catch (err) { 13 | kuro.error(err) 14 | return kuro.edit(msg, `Error \n${err}`) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /commands/gifspeed.js: -------------------------------------------------------------------------------- 1 | exports.run = function(msg, args) { 2 | if (!args[0]) return msg.channel.send('You need to provide a URL to a gif.') 3 | msg.delete() 4 | 5 | let speed = args[1] || 2 6 | 7 | require('request').get({ 8 | url: `https://pilar.moe/aws/api/v1/gif?url=${args[0]}&speed=${speed}`, 9 | encoding: null 10 | }, (err, res, body) => { // eslint-disable-line 11 | try { 12 | let error = JSON.parse(body) 13 | return msg.channel.send(`**Error speeding up gif:**\n${JSON.stringify(error)}`) 14 | } catch (er) { 15 | return msg.channel.sendFile(body, 'x.gif') 16 | } 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /commands/google.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg, args) { 5 | msg.delete() 6 | msg.channel.send('', { 7 | embed: { 8 | type: 'rich', 9 | title: 'Google Search', 10 | description: '[' + args.toString().replace(/,/g, ' ') + '](https://www.google.com/search?hl=en_US&q=' + args.toString().replace(/,/g, '+') + ')', 11 | color: kuro.config.embedColor 12 | } 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /commands/mal.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg, args) { 5 | msg.edit('Loading...') 6 | 7 | const request = require('request') 8 | const parseString = require('xml2js').parseString 9 | const cheerio = require('cheerio') 10 | 11 | let username = kuro.config.MALusername 12 | if (args.length !== 0) username = args[0] 13 | 14 | request(`https://myanimelist.net/malappinfo.php?&status=all&type=anime&u=${username}`, (err, res, body) => { 15 | if (!err) { 16 | parseString(body, function (err, result) { // eslint-disable-line 17 | if (!err) { 18 | request(`https://myanimelist.net/profile/${username}`, (err, res, body) => { // eslint-disable-line 19 | if (!err) { 20 | try { 21 | let description = '\n **Latest updates:**\n' 22 | 23 | let $ = cheerio.load(body) 24 | let data = $('.statistics-updates').find('.data') 25 | 26 | for (let i = 0; i < 3; i++) { 27 | let link = $(data[i]).find('a').attr('href') 28 | let title = $(data[i]).find('a').text() 29 | let thing = $(data[i]).find('div.fn-grey2') 30 | .text() 31 | .split('·')[0] 32 | .trim() 33 | 34 | thing = thing.replace(/\s{1,}/gm,' ') 35 | 36 | description = `${description}[${title}](${link}) - ${thing} \n` 37 | } 38 | 39 | let id = result.myanimelist.myinfo[0].user_id[0] 40 | let img = `https://myanimelist.cdn-dena.com/images/userimages/${id}.jpg` 41 | 42 | description = `${description}\nThis user has spent ${result.myanimelist.myinfo[0].user_days_spent_watching[0]} days watching anime, SUGOI!` 43 | 44 | msg.edit('', { 45 | embed: { 46 | type: 'rich', 47 | title: `${username}'s MyAnimeList Summary`, 48 | url: `https://myanimelist.net/animelist/${username}`, 49 | description: description, 50 | color: kuro.config.embedColor, 51 | fields: [ 52 | { 53 | name: 'Watching', 54 | value: result.myanimelist.myinfo[0].user_watching[0], 55 | inline: true 56 | }, 57 | { 58 | name: 'Completed', 59 | value: result.myanimelist.myinfo[0].user_completed[0], 60 | inline: true 61 | }, 62 | { 63 | name: 'On Hold', 64 | value: result.myanimelist.myinfo[0].user_onhold[0], 65 | inline: true 66 | }, 67 | { 68 | name: 'Dropped', 69 | value: result.myanimelist.myinfo[0].user_dropped[0], 70 | inline: true 71 | }, 72 | { 73 | name: 'Plan to Watch', 74 | value: result.myanimelist.myinfo[0].user_plantowatch[0], 75 | inline: true 76 | } 77 | ], 78 | thumbnail: { url: img } 79 | } 80 | }) 81 | } catch (e) { 82 | kuro.error(e) 83 | msg.delete() 84 | } 85 | } 86 | }) 87 | } 88 | }) 89 | } 90 | }) 91 | } 92 | -------------------------------------------------------------------------------- /commands/manga.js: -------------------------------------------------------------------------------- 1 | const Kitsu = require('kitsu.js') 2 | const kitsu = new Kitsu() 3 | let kuro 4 | 5 | exports.init = function(bot) { kuro = bot } 6 | 7 | exports.run = function(msg, args) { 8 | let search = args.toString().replace(/,/g, ' ') 9 | kitsu.searchManga(search) 10 | .then(result => { 11 | if (result.length === 0) { 12 | return msg.edit(`No results found for: **${search}**`) 13 | } 14 | return prepareEmbed(msg, result[0]) 15 | }) 16 | .catch(err => { 17 | console.error(err) 18 | return msg.edit('There was an error processing the search, please check the console') 19 | }); 20 | } 21 | 22 | function prepareEmbed(msg, item) { 23 | const { slug, synopsis, titles, popularityRank, posterImage, chapterCount, volumeCount, mangaType } = item 24 | const url = `https://kitsu.io/manga/${slug}` 25 | 26 | msg.edit('', { 27 | embed: { 28 | type: 'rich', 29 | title: titles.enJp, 30 | url, 31 | description: `**Synopsis:**\n${synopsis.substring(0, 450)}...`, 32 | color: kuro.config.embedColor, 33 | fields: [ 34 | { 35 | name: '❯ Type', 36 | value: fixCase(mangaType), 37 | inline: true 38 | }, 39 | { 40 | name: '❯ Rank', 41 | value: popularityRank, 42 | inline: true 43 | }, 44 | { 45 | name: '❯ Volumes', 46 | value: volumeCount || '-', 47 | inline: true 48 | }, 49 | { 50 | name: '❯ Chapters', 51 | value: chapterCount || '-', 52 | inline: true 53 | } 54 | ], 55 | author: { 56 | name: 'kitsu.io', 57 | url: 'https://kitsu.io' 58 | }, 59 | thumbnail: { url: posterImage.small } 60 | } 61 | }) 62 | } 63 | 64 | function fixCase(str) { 65 | return str.toLowerCase().replace(/(^| )(\w)/g, s => s.toUpperCase()) 66 | } 67 | -------------------------------------------------------------------------------- /commands/members.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg) { 5 | msg.delete() 6 | msg.channel.send('', { 7 | embed: { 8 | title: msg.guild.name, 9 | description: `Member Count: ${msg.guild.memberCount}`, 10 | color: kuro.config.embedColor 11 | } 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /commands/np.js: -------------------------------------------------------------------------------- 1 | const WebSocket = require('ws'); 2 | 3 | let kuro 4 | let radioInfo 5 | let ws 6 | 7 | const Discord = require('discord.js') 8 | 9 | exports.init = function(bot) { 10 | kuro = bot 11 | this.connectWS() 12 | } 13 | 14 | exports.connectWS = function() { 15 | if (ws) ws.removeAllListeners(); 16 | ws = new WebSocket('wss://listen.moe/api/v2/socket'); 17 | ws.on('message', data => { 18 | try { 19 | if (data) { 20 | const json = JSON.parse(data); 21 | if (json === undefined) return; 22 | if (json.reason !== undefined) return; 23 | radioInfo = json; 24 | } 25 | } catch (err) { 26 | console.error(err) 27 | } 28 | }) 29 | ws.on('close', () => { 30 | setTimeout(() => { this.connectWS(); }, 10000); 31 | }); 32 | ws.on('error', err => { 33 | console.log(err) 34 | }); 35 | } 36 | 37 | exports.run = function(msg) { 38 | if (!radioInfo) return msg.edit('No data available'); 39 | 40 | const artist = `${radioInfo.artist_name}` ? `${radioInfo.artist_name} - ` : ''; 41 | const nowplaying = `${artist}${radioInfo.song_name}`; 42 | const anime = radioInfo.anime_name ? `Anime: ${radioInfo.anime_name}` : ''; 43 | const requestedBy = radioInfo.requested_by 44 | ? /\s/g.test(radioInfo.requested_by) 45 | ? `🎉 **${Discord.escapeMarkdown(radioInfo.requested_by)}** 🎉` 46 | : `Requested by: [${Discord.escapeMarkdown(radioInfo.requested_by)}](https://forum.listen.moe/u/${radioInfo.requested_by})` 47 | : ''; 48 | const song = `${Discord.escapeMarkdown(nowplaying)}\n\n${Discord.escapeMarkdown(anime)}\n${requestedBy}`; 49 | 50 | return msg.edit('', { 51 | embed: { 52 | type: 'rich', 53 | color: kuro.config.embedColor, 54 | fields: [ 55 | { name: 'Now playing', value: song } 56 | ] 57 | } 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /commands/ping.js: -------------------------------------------------------------------------------- 1 | exports.run = function(msg) { 2 | msg.delete() 3 | msg.channel.send('Ping?') 4 | .then(message => { 5 | message.edit(`Pong! (took: ${message.createdTimestamp - msg.createdTimestamp}ms)`) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /commands/playing.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | let _table = 'playing' 3 | 4 | exports.init = function(bot) { 5 | kuro = bot 6 | 7 | // Create the table where we will be storing this module's data 8 | kuro.db.schema.createTableIfNotExists(_table, (table) => { 9 | table.increments() 10 | table.string('game') 11 | }).then(() => { 12 | kuro.db.table(_table).then((row) => { 13 | if (row.length > 0) { 14 | // Seems like data is stored. We should apply the status now 15 | kuro.log(`Setting game status to: ${row[0].game}`) 16 | if (row[0].game === '') return kuro.user.setGame(null) 17 | return kuro.user.setGame(row[0].game) 18 | } 19 | 20 | // Populate it 21 | kuro.db.table(_table) 22 | .insert({ game: '' }) 23 | .then(function() {}) // eslint-disable-line 24 | }) 25 | }).catch((error) => { kuro.error(error) }) 26 | } 27 | 28 | exports.run = function(msg, args) { 29 | if (args.length === 0) { 30 | kuro.user.setGame(null) 31 | this.save('', msg) 32 | return 33 | } 34 | 35 | let text = args.join(' ') 36 | kuro.user.setGame(text) 37 | this.save(text, msg) 38 | } 39 | 40 | exports.save = function(value, msg) { 41 | kuro.db.table(_table).where('id', 1) 42 | .update({ game: value }) 43 | .then(() => { 44 | if (value === '') return kuro.edit(msg, 'You succesfully removed your playing status.') 45 | return kuro.edit(msg, 'Succesfully changed your playing status.') 46 | }) 47 | .catch((error) => { kuro.error(error) }) 48 | } 49 | -------------------------------------------------------------------------------- /commands/purge.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg, args) { 5 | let messagecount = parseInt(args, 10) 6 | msg.channel.fetchMessages({ limit: 100 }) 7 | .then(messages => { 8 | let msgArray = messages.array() 9 | msgArray = msgArray.filter(m => m.author.id === kuro.user.id) 10 | msgArray.length = messagecount + 1 11 | msgArray.map(m => m.delete().catch(console.error)) 12 | } 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /commands/react.js: -------------------------------------------------------------------------------- 1 | exports.run = function(msg, args) { 2 | msg.channel.fetchMessages({ limit: 2 }).then((messages) => { 3 | msg.delete() 4 | messages = messages.array() 5 | 6 | let unicode = ['🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮', '🇯', '🇰', '🇱', '🇲', '🇳', '🇴', '🇵', '🇶', '🇷', '🇸', '🇹', '🇺', '🇻', '🇼', '🇽', '🇾', '🇿', '0⃣', '1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣'] 7 | let alpha = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] 8 | 9 | let chars = args.join('').toLowerCase().split('') 10 | let int = 0; 11 | (function loop() { 12 | messages[1].react(unicode[alpha.indexOf(chars[int])]).then(() => { 13 | int++ 14 | if (chars.length !== int) { 15 | loop() 16 | } 17 | }) 18 | }()) 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /commands/reboot.js: -------------------------------------------------------------------------------- 1 | exports.run = function(msg) { 2 | msg.delete().then(() => process.exit(1)) 3 | } 4 | -------------------------------------------------------------------------------- /commands/regional.js: -------------------------------------------------------------------------------- 1 | exports.run = function(msg, args) { 2 | if (args.length === 0) return msg.delete() 3 | 4 | let text = args.join(' ').toLowerCase().split('') 5 | 6 | let message = '' 7 | for (let i = 0; i < text.length; i++) { 8 | if (text[i] === ' ') message += text[i] 9 | else message = `${message} :regional_indicator_${text[i]}:` 10 | } 11 | 12 | msg.edit(message) 13 | } 14 | -------------------------------------------------------------------------------- /commands/reload.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | exports.run = function(msg) { 4 | msg.delete() 5 | kuro.loadCommands() 6 | } 7 | -------------------------------------------------------------------------------- /commands/s.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | let _msg 3 | let _stickers = {} 4 | let _table = 'stickers' 5 | let assets = './files/stickers' 6 | const fs = require('fs') 7 | const axios = require('axios') 8 | const FormData = require('form-data') 9 | const concat = require('concat-stream') 10 | 11 | exports.init = function(bot) { 12 | kuro = bot 13 | 14 | // Create sticker folder if it doesn't exist 15 | fs.existsSync(assets) || fs.mkdirSync(assets) 16 | 17 | // Create the table where we will be storing this module's data 18 | bot.db.schema.createTableIfNotExists(_table, (table) => { 19 | table.increments() 20 | table.string('name') 21 | table.string('file') 22 | table.string('url') 23 | }).then(() => { 24 | // Lets load up the existing stickers 25 | bot.db.table(_table).then((rows) => { 26 | for (let row of rows) { 27 | if (row.url !== '' && row.url !== undefined) { 28 | _stickers[row.name] = row.url 29 | } else { 30 | _stickers[row.name] = row.file 31 | } 32 | } 33 | }) 34 | }) 35 | .catch((error) => { kuro.error(error) }) 36 | } 37 | 38 | exports.run = function(msg, args) { 39 | _msg = msg 40 | 41 | if (!(args instanceof Array)) { 42 | if (_stickers.hasOwnProperty(args)) return this.sendSticker(args) 43 | return _msg.delete() 44 | } 45 | 46 | let newargs = [] 47 | for (let i = 1; i < args.length; i++) { 48 | newargs.push(args[i]) 49 | } 50 | 51 | // Subcommand? 52 | if (args[0] === 'add') return this.add(newargs) 53 | if (args[0] === 'del') return this.del(newargs) 54 | if (args[0] === 'ren') return this.ren(newargs) 55 | if (args[0] === 'list') return this.list() 56 | if (args[0] === 'migrate') return this.migrate() 57 | 58 | // Not a subcommand, let's see if it's a sticker 59 | if (_stickers.hasOwnProperty(args[0])) return this.sendSticker(args[0]) 60 | 61 | // Ded 62 | _msg.delete() 63 | } 64 | 65 | exports.sendSticker = function(name) { 66 | if (_stickers[name].startsWith('http')) { 67 | _msg.edit({ 68 | embed: { 69 | image: { url: _stickers[name] }, 70 | color: 3290683 71 | } 72 | }); 73 | } else { 74 | let file = `${assets}/${_stickers[name]}` 75 | fs.access(file, fs.constants.R_OK, (err) => { 76 | if (err) return _msg.edit(`**Error:**\n${err}`) 77 | 78 | _msg.delete() 79 | let img = fs.readFileSync(file) 80 | return _msg.channel.sendFile(img, _stickers[name]) 81 | }) 82 | } 83 | } 84 | 85 | exports.add = function(args) { 86 | if (args[0] === undefined) { 87 | kuro.edit(_msg, 'No name provided.') 88 | return 89 | } 90 | 91 | let name = args[0] 92 | 93 | // Is the name of the sticker already used? 94 | if (_stickers.hasOwnProperty(name)) { 95 | kuro.edit(_msg, 'Name already in use.') 96 | return 97 | } 98 | 99 | // Prepare the destination container 100 | let dest = `${assets}/${name}` 101 | let url = '' 102 | 103 | // Stupid discord renaming stuff, breaks everything 104 | let discordFilename = '' 105 | if (args[1] !== undefined) { 106 | url = args[1] 107 | } else { 108 | if (typeof _msg.attachments.first() !== 'undefined') { 109 | if ('proxyURL' in _msg.attachments.first()) { 110 | url = _msg.attachments.first().proxyURL 111 | discordFilename = _msg.attachments.first().filename 112 | } 113 | } 114 | } 115 | 116 | if (url === '') { 117 | // Welp, couldn't figure out a url 118 | kuro.edit(_msg, 'You didnt supply either a url nor attachment, or there was an error with the attachment.') 119 | return 120 | } 121 | 122 | // Try and gather the extension of the file 123 | let re = /(?:\.([^.]+))?$/ 124 | let ext = re.exec(url)[1] 125 | 126 | if (discordFilename !== '') { 127 | ext = re.exec(discordFilename)[1] 128 | } 129 | 130 | if (ext === undefined) { 131 | kuro.edit(_msg, 'The file you are linking or trying to attach doesn\'t have an extension. Kuro needs that thingy. pls fam') 132 | return 133 | } 134 | 135 | dest = `${dest}.${ext}` 136 | this.downloadImage(name, url, dest, ext) 137 | } 138 | 139 | exports.del = function(args) { 140 | if (args[0] === undefined) return kuro.edit(_msg, 'No name provided.') 141 | 142 | if (args[0] in _stickers) { 143 | kuro.db.table(_table) 144 | .where('name', args[0]) 145 | .del() 146 | .then(() => { 147 | delete (_stickers[args[0]]) 148 | return kuro.edit(_msg, 'The sticker was removed.', 1000) 149 | }) 150 | .catch((e) => { kuro.edit(_msg, `Error: \n${e}`, 0) }) 151 | } else { 152 | return kuro.edit(_msg, 'There is no sticker by that name.') 153 | } 154 | } 155 | 156 | exports.ren = function(args) { 157 | if (args[0] === undefined) return kuro.edit(_msg, 'No source sticker supplied.') 158 | if (args[1] === undefined) return kuro.edit(_msg, 'No destination sticker supplied.') 159 | 160 | if (args[0] in _stickers) { 161 | kuro.db.table(_table) 162 | .where('name', args[0]) 163 | .update({ name: args[1] }) 164 | .then(() => { 165 | _stickers[args[1]] = _stickers[args[0]] 166 | delete (_stickers[args[0]]) 167 | return kuro.edit(_msg, 'Sticker renamed.', 1000) 168 | }) 169 | .catch((e) => { kuro.edit(_msg, `Error: \n${e}`, 0) }) 170 | } else { 171 | return kuro.edit(_msg, 'There is no sticker by that name.') 172 | } 173 | } 174 | 175 | exports.list = function() { 176 | let list = '' 177 | for (let sticker in _stickers) { 178 | if ({}.hasOwnProperty.call(_stickers, sticker)) { 179 | list = `${list}${sticker}, ` 180 | } 181 | } 182 | 183 | list = list.substr(0, list.length - 2) 184 | return kuro.edit(_msg, `**__Stickers list__**\n\`\`\`\n${list}\n\`\`\``, 10000) 185 | } 186 | 187 | 188 | exports.downloadImage = function(name, url, dest, ext) { 189 | let saveFile = require('request') 190 | .get(url) 191 | .on('error', (err) => { 192 | kuro.log(err) 193 | _msg.edit(`***Error:*** ${err}`) 194 | }) 195 | .pipe(fs.createWriteStream(dest)) 196 | 197 | saveFile.on('finish', () => { 198 | this.uploadImage(name, dest, ext) 199 | }) 200 | } 201 | 202 | exports.uploadImage = function(name, dest, ext) { 203 | const fd = new FormData() 204 | 205 | fd.append('files[]', fs.createReadStream(dest)) 206 | fd.pipe(concat({ encoding: 'buffer' }, data => { 207 | axios.post('https://safe.moe/api/upload', data, { headers: fd.getHeaders() }) 208 | .then((response) => { 209 | if (response.data.success === false) { 210 | console.error('Error uploading to safe.moe: ', response.description) 211 | kuro.error('Error saving sticker. Check logs') 212 | return this.saveStickerToDB(name, ext, '') 213 | } 214 | 215 | return this.saveStickerToDB(name, ext, response.data.files[0].url) 216 | }) 217 | .catch((err) => console.log(err)) 218 | })) 219 | } 220 | 221 | exports.saveStickerToDB = function(name, ext, url) { 222 | kuro.db.table(_table).insert({ 223 | name: name, 224 | file: `${name}.${ext}`, 225 | url: url 226 | }) 227 | .then(() => { 228 | _stickers[name] = `${name}.${ext}` 229 | if (url !== '') _stickers[name] = url 230 | kuro.edit(_msg, 'Sticker added', 1000) 231 | }) 232 | } 233 | 234 | exports.migrate = function() { 235 | console.log('Starting migration') 236 | _msg.edit('*Starting migration to kuro v4.1.0, this might take a while depending how many stickers you have. Check the console.*') 237 | 238 | try { 239 | 240 | kuro.db.schema.table(_table, (table) => { 241 | table.string('url') 242 | }).then(() => { 243 | // Done for now 244 | kuro.db.table(_table).then((rows) => { 245 | console.log('Found ' + rows.length + ' stickers, starting the upload to safe.moe') 246 | 247 | let counter = 1 248 | for (let row of rows) { 249 | if (row.url === '' || row.url === undefined || row.url === null) { 250 | const fd = new FormData() 251 | 252 | fd.append('files[]', fs.createReadStream(`${assets}/${row.file}`)) 253 | fd.pipe(concat({ encoding: 'buffer' }, data => { 254 | axios.post('https://safe.moe/api/upload', data, { headers: fd.getHeaders() }) 255 | .then((response) => { 256 | if (response.data.success === false) { 257 | console.error('Error uploading to safe.moe: ', response.description) 258 | } else { 259 | kuro.db.table(_table).where('id', row.id).update( 260 | { url: response.data.files[0].url } 261 | ) 262 | .then(() => { 263 | console.log('Finished uploading ' + counter + '/' + rows.length + ': ' + row.name) 264 | counter++ 265 | _stickers[row.name] = response.data.files[0].url 266 | if (counter > rows.length) { 267 | console.log('Migration finished, you can keep using Kuro like you normally would') 268 | _msg.edit('*Migration finished, you can keep using Kuro like you normally would*') 269 | } 270 | }) 271 | } 272 | }) 273 | .catch((err) => console.log(err)) 274 | })) 275 | } 276 | } 277 | }) 278 | }) 279 | 280 | .catch((error) => { kuro.error(error) }) 281 | } catch (e) { 282 | kuro.error(e) 283 | } 284 | } 285 | 286 | exports.stickerCount = function() { 287 | return Object.keys(_stickers).length 288 | } 289 | 290 | -------------------------------------------------------------------------------- /commands/shinobu.js: -------------------------------------------------------------------------------- 1 | exports.run = function(msg, args) { 2 | if (args[0] === '1') return this.shinobuFull(msg) 3 | if (args[0] === '2') return this.shinobuLegs(msg) 4 | if (args[0] === '3') return this.shinobuLeg(msg) 5 | return msg.delete() 6 | } 7 | 8 | exports.shinobuFull = function(msg) { 9 | msg.delete() 10 | 11 | .then(() => msg.channel.send(` 12 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:1s5:265870167597121536><:1s6:265870167362240513><:1s7:265870167349657602><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:1s27:265870167446126612><:1s28:265870167358177282> 13 | <:sB:265870166972170240><:sB:265870166972170240><:2s3:265870239575703552><:2s4:265870239789481984><:2s5:265870240615890944><:2s6:265870240192135168><:2s7:265870240204849152><:2s8:265870240137740289><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:2s17:265870240062111745><:2s18:265870240276152320><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:2s25:265870240108380174><:2s26:265870241047904256><:2s27:265870240536068096><:2s28:265870240557039616> 14 | `)) 15 | 16 | .then(() => msg.channel.send(` 17 | <:sB:265870166972170240><:3s2:265870322094440448><:3s3:265870322161418240><:3s4:265870322215944202><:3s5:265870322467733504><:3s6:265870322593562624><:3s7:265870322367070214><:3s8:265870322811535361><:sB:265870166972170240><:sB:265870166972170240><:3s11:265870322819923978><:3s12:265870322379653122><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:3s17:265870322945884160><:3s18:265870322543099926><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:3s25:265870322970918912><:3s26:265870322912329739><:3s27:265870322815860747><:3s28:265870323075776512> 18 | <:4s1:265870391686201345><:4s2:265870392021876746><:4s3:265870391745052673><:4s4:265870391984128001><:4s5:265870392441176064><:4s6:265870392508284928><:4s7:265870392411947009><:4s8:265870392739102720><:4s9:265870392571330561><:sB:265870166972170240><:4s11:265870392785240064><:4s12:265870392864800768><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:4s17:265870392894291968><:4s18:265870393565380609><:4s19:265870392990760960><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:4s25:265870393053675520><:4s26:265870393955450880><:4s27:265870392873189378><:sB:265870166972170240> 19 | `)) 20 | 21 | .then(() => msg.channel.send(` 22 | <:5s1:265870489346506773><:5s2:265870489543507969><:5s3:265870489703022592><:5s4:265870489715605504><:5s5:265870489975521280><:5s6:265870489937772544><:5s7:265870489719799811><:5s8:265870490214727680><:5s9:265870490231504896><:sB:265870166972170240><:5s11:265870490273447936><:5s12:265870490130710529><:5s13:265870490197819393><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:5s17:265870490436894720><:5s18:265870490281836545><:5s19:265870490218921986><:5s20:265870490743078912><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:5s25:265870490596409344><:5s26:265870490722107392><:5s27:265870490743209984><:sB:265870166972170240> 23 | <:6s1:265870596796055552><:6s2:265870596645191682><:6s3:265870597039325194><:6s4:265870597228199936><:6s5:265870597400035328><:6s6:265870597580521472><:6s7:265870598020792320><:6s8:265870597739773963><:6s9:265870597907677185><:6s10:265870597668601867><:6s11:265870598276644864><:6s12:265870598498942976><:6s13:265870597962203136><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:6s18:265870597727191042><:6s19:265870598243221514><:6s20:265870597999820801><:6s21:265870598746406912><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:6s25:265870598377439232><:6s26:265870597857214465><:6s27:265870597773459457><:sB:265870166972170240> 24 | `)) 25 | 26 | .then(() => msg.channel.send(` 27 | <:7s1:265870654899879936><:7s2:265870655159926794><:7s3:265870655428231168><:7s4:265870655780552704><:7s5:265870655520505856><:7s6:265870655646334976><:7s7:265870655659048960><:7s8:265870655851986945><:7s9:265870655851986944><:7s10:265870655919095808><:7s11:265870655969296384><:7s12:265870980650500097><:7s13:265870981074124800><:7s14:265870980843307020><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:7s18:265870981220925440><:7s19:265870981157879809><:7s20:265870981174788097><:7s21:265870981531172864><:7s22:265870981245960193><:sB:265870166972170240><:sB:265870166972170240><:7s25:265870981338365963><:7s26:265870981455675392><:7s27:265870981497618433><:sB:265870166972170240> 28 | <:sB:265870166972170240><:8s2:265871214327627796><:8s3:265871214675755018><:8s4:265871214810103819><:8s5:265871214940127232><:8s6:265871214738669570><:8s7:265871214919155713><:8s8:265871214898184203><:8s9:265871215577661440><:8s10:265871214919155725><:8s11:265871215195979777><:8s12:265871215434924032><:8s13:265871215170813953><:8s14:265871215535587328><:8s15:265871215942565890><:sB:265870166972170240><:8s17:265871215569141760><:sB:265870166972170240><:sB:265870166972170240><:8s20:265871215384592386><:8s21:265871215363620866><:8s22:265871215560753153><:sB:265870166972170240><:sB:265870166972170240><:8s25:265871215422472193><:8s26:265871215338455041><:8s27:265871215795765248><:sB:265870166972170240> 29 | `)) 30 | 31 | .then(() => msg.channel.send(` 32 | <:sB:265870166972170240><:sB:265870166972170240><:9s3:265871310406680579><:9s4:265871310532509697><:9s5:265871310616264705><:9s6:265871310847082496><:9s7:265871311010529280><:9s8:265871311010660353><:9s9:265871311316844544><:9s10:265871310922579969><:9s11:265871311182626817><:9s12:265871311337684992><:9s13:265871311673229312><:9s14:265871311559983104><:9s15:265871312059105280><:9s16:265871311748857857><:9s17:265871311803252736><:9s18:265871312088596480><:9s19:265871311711109122><:9s20:265871312021487616><:9s21:265871478195486720><:9s22:265871477847359490><:9s23:265871478669574154><:sB:265870166972170240><:9s25:265871478682025995><:9s26:265871478661185537><:9s27:265871478652665859><:sB:265870166972170240> 33 | <:sB:265870166972170240><:10s2:265871574450700288><:10s3:265871574660415488><:10s4:265871574756884480><:10s5:265871574677061633><:10s6:265871574849159171><:10s7:265871574614147073><:10s8:265871574861742081><:10s9:265871575146823680><:10s10:265871574798827521><:10s11:265871575151149066><:10s12:265871574979051522><:10s13:265871575239229441><:10s14:265871575293755403><:10s15:265871575402676224><:10s16:265871575411195905><:10s17:265871575520247808><:10s18:265871575792877568><:10s19:265871575805460480><:10s20:265871575784488960><:10s21:265871575750934530><:10s22:265871575889346570><:10s23:265871576002461696><:10s24:265871575855661066><:10s25:265871575922769920><:10s26:265871575914381312><:10s27:265871575931289600><:sB:265870166972170240> 34 | `)) 35 | 36 | .then(() => msg.channel.send(` 37 | <:sB:265870166972170240><:11s2:265871631572795393><:11s3:265871632260661248><:11s4:265871632109797387><:11s5:265871632453599232><:11s6:265871633242259456><:11s7:265871632512450560><:11s8:265871632546004992><:11s9:265871632512450571><:11s10:265871632730423296><:11s11:265871632713646080><:11s12:265871632839475200><:11s13:265871632889806848><:11s14:265871632546005004><:11s15:265871632684417027><:11s16:265871633078681600><:11s17:265871633195991040><:11s18:265871633200316416><:11s19:265871633284202496><:11s20:265871759700525056><:11s21:265871759427764225><:11s22:265871759780216832><:11s23:265871759520169995><:11s24:265871759868297216><:11s25:265871759956377600><:11s26:265871759637610497><:11s27:265871760019161088><:sB:265870166972170240> 38 | <:sB:265870166972170240><:12s2:265871845755060235><:12s3:265871846245793793><:12s4:265871846208045067><:12s5:265871846644252672><:12s6:265871846426017793><:12s7:265871846732333057><:12s8:265871846791053332><:12s9:265871846736527361><:12s10:265871846639927297><:12s11:265871846937722880><:12s12:265871846983860224><:12s13:265871847155826688><:12s14:265871847105626113><:12s15:265871846933659649><:12s16:265871847155826689><:12s17:265871847319535616><:12s18:265871847315341312><:12s19:265871847394902016><:12s20:265871847436845056><:12s21:265871847462010880><:12s22:265871847487176714><:12s23:265871847323598850><:12s24:265871847088848898><:12s25:265871847503953920><:12s26:265871847583645696><:12s27:265871847587971072><:sB:265870166972170240> 39 | `)) 40 | 41 | .then(() => msg.channel.send(` 42 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:13s4:265871914038198275><:13s5:265871914424205313><:13s6:265871914470342656><:13s7:265871914201776140><:13s8:265871914516480001><:13s9:265871914658955265><:13s10:265871914881384448><:13s11:265871914889773056><:13s12:265871914998693889><:13s13:265871915158077441><:13s14:265871914763812865><:13s15:265871915183374336><:13s16:265871915288231936><:13s17:265871949555695616><:13s18:265871949773799424><:13s19:265871949559889921><:13s20:265872185787285505><:13s21:265872186227556363><:13s22:265872186160578561><:13s23:265872186701512705><:13s24:265872186831536128><:13s25:265872186676346881><:13s26:265872186814889985><:13s27:265872186533740545><:sB:265870166972170240> 43 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:14s4:265872320776634373><:14s5:265872321322024961><:14s6:265872321414168576><:14s7:265872321208647681><:14s8:265872321418493955><:14s9:265872321644855296><:14s10:265872321640792064><:14s11:265872321703706634><:14s12:265872322051833859><:14s13:265872321401585666><:14s14:265872321867153408><:14s15:265872321682604036><:14s16:265872321951170560><:14s17:265872322022342666><:14s18:265872322047508480><:14s19:265872322211217408><:14s20:265872322072805377><:14s21:265872321762295810><:14s22:265872322240446464><:14s23:265872322207023114><:14s24:265872321905033220><:14s25:265872322018279425><:14s26:265872322202697729><:14s27:265872322022342658><:sB:265870166972170240> 44 | `)) 45 | 46 | .then(() => msg.channel.send(` 47 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:15s5:265872376711872514><:15s6:265872377391349777><:15s7:265872377588482048><:15s8:265872377206931458><:sB:265870166972170240><:15s10:265872376993021955><:15s11:265872377387155456><:15s12:265872377408126976><:15s13:265872377433292800><:15s14:265872377345343490><:15s15:265872377542344704><:15s16:265872377286492161><:15s17:265872377504595979><:15s18:265872377617973249><:15s19:265872377898991626><:15s20:265872377743802369><:15s21:265872377991135232><:15s22:265872377903054848><:15s23:265872377970294784><:15s24:265873317494390785><:15s25:265872760805261313><:15s26:265872760927027209><:sB:265870166972170240><:sB:265870166972170240> 48 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:16s12:265873386721247232><:16s13:265873387111317504><:16s14:265873387300192256><:16s15:265873387270832128><:16s16:265873387224694786><:16s17:265873387526684672><:16s18:265873387040014338><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240> 49 | `)) 50 | 51 | .then(() => msg.channel.send(` 52 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:17s13:265873435509391360><:17s14:265873435396276225><:17s15:265873435639545856><:17s16:265873435735883786><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240> 53 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240> 54 | `)) 55 | } 56 | 57 | exports.shinobuLegs = function(msg) { 58 | msg.delete() 59 | 60 | .then(() => msg.channel.send(` 61 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:1s27:265870167446126612><:1s28:265870167358177282> 62 | <:sB:265870166972170240><:sB:265870166972170240><:2s17:265870240062111745><:2s18:265870240276152320><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:2s25:265870240108380174><:2s26:265870241047904256><:2s27:265870240536068096><:2s28:265870240557039616> 63 | <:sB:265870166972170240><:sB:265870166972170240><:3s17:265870322945884160><:3s18:265870322543099926><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:3s25:265870322970918912><:3s26:265870322912329739><:3s27:265870322815860747><:3s28:265870323075776512> 64 | <:sB:265870166972170240><:sB:265870166972170240><:4s17:265870392894291968><:4s18:265870393565380609><:4s19:265870392990760960><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:4s25:265870393053675520><:4s26:265870393955450880><:4s27:265870392873189378><:sB:265870166972170240> 65 | <:sB:265870166972170240><:sB:265870166972170240><:5s17:265870490436894720><:5s18:265870490281836545><:5s19:265870490218921986><:5s20:265870490743078912><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:5s25:265870490596409344><:5s26:265870490722107392><:5s27:265870490743209984><:sB:265870166972170240> 66 | `)) 67 | 68 | .then(() => msg.channel.send(` 69 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:6s18:265870597727191042><:6s19:265870598243221514><:6s20:265870597999820801><:6s21:265870598746406912><:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:6s25:265870598377439232><:6s26:265870597857214465><:6s27:265870597773459457><:sB:265870166972170240> 70 | <:sB:265870166972170240><:sB:265870166972170240><:sB:265870166972170240><:7s18:265870981220925440><:7s19:265870981157879809><:7s20:265870981174788097><:7s21:265870981531172864><:7s22:265870981245960193><:sB:265870166972170240><:sB:265870166972170240><:7s25:265870981338365963><:7s26:265870981455675392><:7s27:265870981497618433><:sB:265870166972170240> 71 | <:8s15:265871215942565890><:sB:265870166972170240><:8s17:265871215569141760><:sB:265870166972170240><:sB:265870166972170240><:8s20:265871215384592386><:8s21:265871215363620866><:8s22:265871215560753153><:sB:265870166972170240><:sB:265870166972170240><:8s25:265871215422472193><:8s26:265871215338455041><:8s27:265871215795765248><:sB:265870166972170240> 72 | <:9s15:265871312059105280><:9s16:265871311748857857><:9s17:265871311803252736><:9s18:265871312088596480><:9s19:265871311711109122><:9s20:265871312021487616><:9s21:265871478195486720><:9s22:265871477847359490><:9s23:265871478669574154><:sB:265870166972170240><:9s25:265871478682025995><:9s26:265871478661185537><:9s27:265871478652665859><:sB:265870166972170240> 73 | <:10s15:265871575402676224><:10s16:265871575411195905><:10s17:265871575520247808><:10s18:265871575792877568><:10s19:265871575805460480><:10s20:265871575784488960><:10s21:265871575750934530><:10s22:265871575889346570><:10s23:265871576002461696><:10s24:265871575855661066><:10s25:265871575922769920><:10s26:265871575914381312><:10s27:265871575931289600><:sB:265870166972170240> 74 | `)) 75 | 76 | .then(() => msg.channel.send(` 77 | <:11s15:265871632684417027><:11s16:265871633078681600><:11s17:265871633195991040><:11s18:265871633200316416><:11s19:265871633284202496><:11s20:265871759700525056><:11s21:265871759427764225><:11s22:265871759780216832><:11s23:265871759520169995><:11s24:265871759868297216><:11s25:265871759956377600><:11s26:265871759637610497><:11s27:265871760019161088> 78 | <:12s15:265871846933659649><:12s16:265871847155826689><:12s17:265871847319535616><:12s18:265871847315341312><:12s19:265871847394902016><:12s20:265871847436845056><:12s21:265871847462010880><:12s22:265871847487176714><:12s23:265871847323598850><:12s24:265871847088848898><:12s25:265871847503953920><:12s26:265871847583645696><:12s27:265871847587971072> 79 | <:13s15:265871915183374336><:13s16:265871915288231936><:13s17:265871949555695616><:13s18:265871949773799424><:13s19:265871949559889921><:13s20:265872185787285505><:13s21:265872186227556363><:13s22:265872186160578561><:13s23:265872186701512705><:13s24:265872186831536128><:13s25:265872186676346881><:13s26:265872186814889985><:13s27:265872186533740545> 80 | <:14s15:265872321682604036><:14s16:265872321951170560><:14s17:265872322022342666><:14s18:265872322047508480><:14s19:265872322211217408><:14s20:265872322072805377><:14s21:265872321762295810><:14s22:265872322240446464><:14s23:265872322207023114><:14s24:265872321905033220><:14s25:265872322018279425><:14s26:265872322202697729><:14s27:265872322022342658> 81 | <:15s15:265872377542344704><:15s16:265872377286492161><:15s17:265872377504595979><:15s18:265872377617973249><:15s19:265872377898991626><:15s20:265872377743802369><:15s21:265872377991135232><:15s22:265872377903054848><:15s23:265872377970294784><:15s24:265873317494390785><:15s25:265872760805261313><:15s26:265872760927027209><:sB:265870166972170240> 82 | `)) 83 | } 84 | 85 | exports.shinobuLeg = function(msg) { 86 | msg.edit(` 87 | <:sB:265870166972170240><:sB:265870166972170240><:1s27:265870167446126612><:1s28:265870167358177282> 88 | <:2s25:265870240108380174><:2s26:265870241047904256><:2s27:265870240536068096><:2s28:265870240557039616> 89 | <:3s25:265870322970918912><:3s26:265870322912329739><:3s27:265870322815860747><:3s28:265870323075776512> 90 | <:4s25:265870393053675520><:4s26:265870393955450880><:4s27:265870392873189378><:sB:265870166972170240> 91 | <:5s25:265870490596409344><:5s26:265870490722107392><:5s27:265870490743209984><:sB:265870166972170240> 92 | <:6s25:265870598377439232><:6s26:265870597857214465><:6s27:265870597773459457><:sB:265870166972170240> 93 | <:7s25:265870981338365963><:7s26:265870981455675392><:7s27:265870981497618433><:sB:265870166972170240> 94 | <:8s25:265871215422472193><:8s26:265871215338455041><:8s27:265871215795765248> 95 | <:9s25:265871478682025995><:9s26:265871478661185537><:9s27:265871478652665859> 96 | <:10s25:265871575922769920><:10s26:265871575914381312><:10s27:265871575931289600> 97 | <:11s25:265871759956377600><:11s26:265871759637610497><:11s27:265871760019161088> 98 | <:12s25:265871847503953920><:12s26:265871847583645696><:12s27:265871847587971072> 99 | <:13s25:265872186676346881><:13s26:265872186814889985><:13s27:265872186533740545> 100 | <:14s25:265872322018279425><:14s26:265872322202697729><:14s27:265872322022342658> 101 | <:15s25:265872760805261313><:15s26:265872760927027209><:sB:265870166972170240> 102 | `) 103 | } 104 | -------------------------------------------------------------------------------- /commands/smug.js: -------------------------------------------------------------------------------- 1 | exports.run = function(msg) { 2 | msg.edit('*Loading...*') 3 | require('request').get('http://smugs.safe.moe/api/v1/i/r', (error, response, body) => { 4 | if (!error && response.statusCode === 200) { 5 | try { 6 | JSON.parse(body) 7 | } catch (e) { 8 | return msg.reply('***API ERROR***') 9 | } 10 | const resp = JSON.parse(body) 11 | msg.edit({ 12 | embed: { 13 | image: { url: `https://smugs.safe.moe/${resp.url}` } 14 | } 15 | }); 16 | } 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /commands/stats.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg) { 5 | let pjson = require('../package.json') 6 | 7 | let version = `v${pjson.version.toString()}` 8 | let uptime = secondsToString(process.uptime()).toString() 9 | let modules = Object.keys(kuro.modules).length.toString() 10 | let memory = `${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB` 11 | 12 | // Showing the amount of stickers to demonstrate accessing other module's information 13 | let stickers = kuro.modules.s.stickerCount().toString() 14 | let tags = kuro.modules.tag.tagsCount().toString() 15 | 16 | msg.edit('', { 17 | embed: { 18 | type: 'rich', 19 | description: '[Kurobot Stats](https://github.com/kanadeko/Kuro)', 20 | color: kuro.config.embedColor, 21 | fields: [ 22 | { name: '❯ Version', value: version, inline: true }, 23 | { name: '❯ Ram usage', value: memory, inline: true }, 24 | { name: '❯ Modules', value: modules, inline: true }, 25 | { name: '❯ Stickers', value: stickers, inline: true }, 26 | { name: '❯ Tags', value: tags, inline: true } 27 | ], 28 | thumbnail: { url: 'https://i.imgur.com/sVVcwJd.png' }, 29 | footer: { text: `Uptime: ${uptime}` } 30 | } 31 | }) 32 | } 33 | 34 | function secondsToString(seconds) { 35 | seconds = Math.trunc(seconds) 36 | let numdays = Math.floor((seconds % 31536000) / 86400) 37 | let numhours = Math.floor(((seconds % 31536000) % 86400) / 3600) 38 | let numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60) 39 | let numseconds = (((seconds % 31536000) % 86400) % 3600) % 60 40 | return `${numdays} days ${numhours} hours ${numminutes} minutes ${numseconds} seconds` 41 | } 42 | -------------------------------------------------------------------------------- /commands/status.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | let _table = 'status' 3 | 4 | exports.init = function(bot) { 5 | kuro = bot 6 | 7 | // Create the table where we will be storing this module's data 8 | kuro.db.schema.createTableIfNotExists(_table, (table) => { 9 | table.increments() 10 | table.string('status') 11 | }).then(() => { 12 | kuro.db.table(_table).then((row) => { 13 | if (row.length > 0) { 14 | // Seems like data is stored. We should apply the status now 15 | kuro.log(`Setting offline status to: ${row[0].status}`) 16 | kuro.user.setStatus(row[0].status) 17 | return 18 | } 19 | 20 | // Populate it 21 | kuro.db.table(_table).insert({ status: 'online' }).then(function() {}) // eslint-disable-line 22 | }) 23 | }).catch((error) => { kuro.error(error) }) 24 | } 25 | 26 | exports.run = function(msg, args) { 27 | if (args.length === 0) return kuro.edit(msg, `Your offline status is: ${msg.client.status}`) 28 | 29 | if (args[0] !== 'idle' && args[0] !== 'online' && args[0] !== 'dnd' && args[0] !== 'invisible') { 30 | return kuro.edit(msg, 'Wrong option. You need to specify idle|online|dnd|invisible') 31 | } 32 | 33 | kuro.db.table(_table).where('id', 1) 34 | .update({ status: args[0] }) 35 | .then(() => { 36 | kuro.user.setStatus(args[0]) 37 | return kuro.edit(msg, `Next time you are offline your status will be set to: ${args[0]}`) 38 | }) 39 | .catch((error) => { kuro.error(error) }) 40 | } 41 | -------------------------------------------------------------------------------- /commands/tag.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | let _msg 3 | let _table = 'tags' 4 | let _tags = {} 5 | 6 | exports.init = function(bot) { 7 | kuro = bot 8 | 9 | // Create the table where we will be storing this module's data 10 | bot.db.schema.createTableIfNotExists(_table, (table) => { 11 | table.increments() 12 | table.string('name') 13 | table.string('content') 14 | }).then(() => { 15 | // Lets load up the existing tags 16 | bot.db.table(_table).then((rows) => { 17 | for (let row of rows) { 18 | _tags[row.name] = row.content 19 | } 20 | }) 21 | }) 22 | .catch((error) => { kuro.error(error) }) 23 | } 24 | 25 | exports.run = function(msg, args) { 26 | _msg = msg 27 | 28 | let newargs = [] 29 | for (let i = 1; i < args.length; i++) { 30 | newargs.push(args[i]) 31 | } 32 | 33 | // Subcommand? 34 | if (args[0] === 'add') return this.add(newargs) 35 | if (args[0] === 'del') return this.del(newargs) 36 | if (args[0] === 'ren') return this.ren(newargs) 37 | if (args[0] === 'list') return this.list() 38 | 39 | // Not a subcommand, let's see if it's a tag and send it 40 | if (_tags.hasOwnProperty(args[0])) return _msg.edit(_tags[args[0]]) 41 | 42 | // Not a subcommand, not a tag, let's return the whole list 43 | return this.list() 44 | } 45 | 46 | exports.add = function(args) { 47 | if (args[0] === undefined) { 48 | kuro.edit(_msg, 'No name provided.') 49 | return 50 | } 51 | 52 | if (args[1] === undefined) { 53 | kuro.edit(_msg, 'No content provided.') 54 | return 55 | } 56 | 57 | let name = args[0] 58 | let content = args.slice(1).join(' ') 59 | 60 | // Is the name of the sticker already used? 61 | if (_tags.hasOwnProperty(name)) { 62 | kuro.edit(_msg, 'Name already in use.') 63 | return 64 | } 65 | 66 | kuro.db.table(_table).insert({ 67 | name: name, 68 | content: content 69 | }).then(() => { 70 | _tags[name] = content 71 | kuro.edit(_msg, 'Tag added', 1000) 72 | }) 73 | .catch((error) => { kuro.error(error) }) 74 | } 75 | 76 | exports.del = function(args) { 77 | if (args[0] === undefined) return kuro.edit(_msg, 'No name provided.') 78 | 79 | if (args[0] in _tags) { 80 | kuro.db.table(_table) 81 | .where('name', args[0]) 82 | .del() 83 | .then(() => { 84 | delete (_tags[args[0]]) 85 | return kuro.edit(_msg, 'The tag was removed.', 1000) 86 | }) 87 | .catch((e) => { return kuro.edit(_msg, `Error: \n${e}`, 0) }) 88 | } else { 89 | return kuro.edit(_msg, 'There is no tag by that name.') 90 | } 91 | } 92 | 93 | exports.ren = function(args) { 94 | if (args[0] === undefined) return kuro.edit(_msg, 'No source tag supplied.') 95 | if (args[1] === undefined) return kuro.edit(_msg, 'No destination tag supplied.') 96 | 97 | if (args[0] in _tags) { 98 | kuro.db.table(_table).where('name', args[0]) 99 | .update({ name: args[1] }) 100 | .then(() => { 101 | _tags[args[1]] = _tags[args[0]] 102 | delete (_tags[args[0]]) 103 | return kuro.edit(_msg, 'Tag renamed.', 1000) 104 | }) 105 | .catch((e) => { kuro.edit(_msg, `Error: \n${e}`, 0) }) 106 | } else { 107 | return kuro.edit(_msg, 'There is no tag by that name.') 108 | } 109 | } 110 | 111 | exports.list = function() { 112 | let list = '' 113 | for (let tag in _tags) { 114 | if ({}.hasOwnProperty.call(_tags, tag)) { 115 | list = `${list} ${tag}, ` 116 | } 117 | } 118 | 119 | list = list.substr(0, list.length - 2) 120 | _msg.edit(`**__Tags list__**\n\`\`\`\n${list}\n\`\`\``) 121 | } 122 | 123 | exports.tagsCount = function() { 124 | return Object.keys(_tags).length 125 | } 126 | -------------------------------------------------------------------------------- /commands/tl.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg) { 5 | msg.channel.fetchMessages({ limit: 2 }) 6 | .then((messages) => { 7 | messages = messages.array() 8 | require('google-translate-api')(messages[1].content, { to: 'en' }).then(res => { 9 | msg.edit(`Translated from \`${res.from.language.iso}\` | ${res.text}`) 10 | }).catch(err => { 11 | kuro.log(err) 12 | msg.delete() 13 | }) 14 | }) 15 | .catch(e => kuro.error(e)) 16 | } 17 | -------------------------------------------------------------------------------- /commands/uptime.js: -------------------------------------------------------------------------------- 1 | let kuro 2 | exports.init = function(bot) { kuro = bot } 3 | 4 | exports.run = function(msg) { 5 | msg.edit('', { 6 | embed: { 7 | title: 'Kurobot', 8 | description: `Uptime: ${secondsToString(process.uptime())}`, 9 | color: kuro.config.embedColor 10 | } 11 | }) 12 | } 13 | 14 | function secondsToString(seconds) { 15 | seconds = Math.trunc(seconds) 16 | let numdays = Math.floor((seconds % 31536000) / 86400) 17 | let numhours = Math.floor(((seconds % 31536000) % 86400) / 3600) 18 | let numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60) 19 | let numseconds = (((seconds % 31536000) % 86400) % 3600) % 60 20 | return `${numdays} days ${numhours} hours ${numminutes} minutes ${numseconds} seconds` 21 | } 22 | -------------------------------------------------------------------------------- /commands/user.js: -------------------------------------------------------------------------------- 1 | exports.run = function(msg, args) { 2 | if (!args[0]) return msg.delete() 3 | 4 | let id = args[0].replace(/<@!|<@/, '').replace('>', '') 5 | let user = msg.guild.members.get(id) 6 | if (!user) return msg.delete() 7 | 8 | return msg.edit({ 9 | embed: { 10 | title: `User info for ${user.user.username}`, 11 | thumbnail: { url: user.user.avatarURL }, 12 | fields: [ 13 | { 14 | name: 'Username', 15 | value: user.user.username, 16 | inline: true 17 | }, 18 | { 19 | name: 'Discriminator', 20 | value: user.user.discriminator, 21 | inline: true 22 | }, 23 | { 24 | name: 'User ID', 25 | value: user.user.id, 26 | inline: true 27 | }, 28 | { 29 | name: 'Nickname', 30 | value: user.nickname ? user.nickname : 'N/A', 31 | inline: true 32 | }, 33 | { 34 | name: 'Account Creation Date', 35 | value: user.user.createdAt.toLocaleDateString(), 36 | inline: true 37 | }, 38 | { 39 | name: 'Guild Join Date', 40 | value: user.joinedAt.toLocaleDateString(), 41 | inline: true 42 | }, 43 | { 44 | name: 'Current Status', 45 | value: user.presence.status, 46 | inline: true 47 | } 48 | ] 49 | } 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /config.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "token": "", 3 | "prefix": "!", 4 | "MALusername": "", 5 | 6 | "commandError": { 7 | "sendToModule": true, 8 | "module": "s", 9 | "function": "run" 10 | }, 11 | "embedColor": 15473237 12 | } 13 | -------------------------------------------------------------------------------- /kuro.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | if (!fs.existsSync(path.join(__dirname, 'config.json'))) { 5 | console.log('config.json file not found, please run kuro-cli or create it on your own') 6 | process.exit() 7 | } 8 | 9 | const config = require('./config.json') 10 | const Discord = require('discord.js') 11 | const chalk = require('chalk') 12 | const knex = require('knex')({ 13 | client: 'sqlite3', 14 | connection: { filename: path.join(__dirname, 'db') }, 15 | useNullAsDefault: true 16 | }) 17 | 18 | let filesDirectory = path.join(__dirname, 'files') 19 | fs.existsSync(filesDirectory) || fs.mkdirSync(filesDirectory) 20 | 21 | // Initializing the ultimate tan 22 | const kuro = new Discord.Client() 23 | 24 | // When ready 25 | kuro.on('ready', () => { 26 | // Create database if it doesn't exist 27 | fs.exists('db', (exists) => exists || fs.writeFile('db', '')) 28 | 29 | // Getting the database ready 30 | kuro.db = knex 31 | 32 | // Making config available on every module 33 | kuro.config = config 34 | kuro.loadCommands() 35 | kuro.user.setAFK(true) 36 | kuro.log('Kuro is ready!', 'green') 37 | }) 38 | 39 | kuro.on('message', (msg) => { 40 | // Ignore if the message is not ours 41 | if (msg.author.id !== kuro.user.id) return 42 | 43 | // Ignore if the message doesn't start with our prefix 44 | if (!msg.content.startsWith(config.prefix)) return 45 | 46 | // Ignore if empty command 47 | if (msg.content.length === config.prefix.length) return 48 | 49 | // Get all the arguments 50 | let tmp = msg.content.substring(config.prefix.length, msg.length).split(' ') 51 | let args = [] 52 | 53 | for (let i = 1; i < tmp.length; i++) { 54 | args.push(tmp[i]) 55 | } 56 | 57 | // Store the command separately 58 | let cmd = tmp[0] 59 | 60 | if (kuro.modules.hasOwnProperty(cmd)) return kuro.modules[cmd].run(msg, args) 61 | if (config.commandError.sendToModule === true) { 62 | return kuro.modules[config.commandError.module][config.commandError.function](msg, cmd) 63 | } 64 | 65 | return msg.delete() 66 | }) 67 | 68 | kuro.on('disconnect', () => { 69 | kuro.error('CLIENT: Disconnected!') 70 | process.exit() 71 | }) 72 | 73 | kuro.on('reconnecting', () => { kuro.log('CLIENT: Reconnecting...', 'green') }) 74 | 75 | kuro.loadCommands = function() { 76 | kuro.modules = {} 77 | 78 | // Load up all the modules 79 | fs.readdirSync('./commands/').forEach((file) => { 80 | let name = file.slice(0, -3) 81 | 82 | delete require.cache[require.resolve(`./commands/${file}`)] 83 | 84 | try { 85 | kuro.modules[name] = require(`./commands/${file}`) 86 | if (kuro.modules[name].hasOwnProperty('init')) { 87 | kuro.modules[name].init(kuro) 88 | } 89 | 90 | kuro.log(`Module ${name} is ready`) 91 | } catch (e) { 92 | kuro.error(`Error in module ${name}:\n${e.stack}`) 93 | } 94 | }) 95 | } 96 | 97 | kuro.edit = function(msg, content, timeout = 3000) { 98 | if (timeout === 0) return msg.edit(content).catch(console.error) 99 | 100 | return msg.edit(content).then(() => { 101 | setTimeout(() => msg.delete().catch(console.error), timeout) 102 | }) 103 | } 104 | 105 | kuro.log = function(msg, color) { 106 | if (color === undefined) console.log(`[Kuro]: ${msg}`) 107 | else console.log(chalk[color](`[Kuro]: ${msg}`)) 108 | } 109 | 110 | kuro.error = function(msg) { 111 | console.log(chalk.red(`[Kuro]: ${msg}`)) 112 | } 113 | 114 | kuro.log('Starting...', 'green') 115 | kuro.login(config.token) 116 | 117 | process.on('unhandledRejection', err => { 118 | kuro.error(`Uncaught Promise Error:\n${err.stack}`) 119 | }) 120 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kuro", 3 | "version": "4.1.6", 4 | "description": "Self bot with utilities", 5 | "author": "kanadeko", 6 | "main": "kuro.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/kanadeko/Kuro" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/kanadeko/Kuro/issues" 13 | }, 14 | "engines": { 15 | "node": ">=6.0.0" 16 | }, 17 | "license": "MIT", 18 | "dependencies": { 19 | "axios": "^0.15.3", 20 | "chalk": "^1.1.3", 21 | "cheerio": "^0.22.0", 22 | "concat-stream": "^1.6.0", 23 | "discord.js": "^11.1.0", 24 | "form-data": "^2.1.4", 25 | "google-translate-api": "^2.2.2", 26 | "jimp": "^0.2.27", 27 | "kitsu.js": "^1.1.0", 28 | "knex": "^0.12.6", 29 | "request": "^2.74.0", 30 | "sqlite3": "^3.1.8", 31 | "xml2js": "^0.4.17" 32 | } 33 | } 34 | --------------------------------------------------------------------------------