├── .gitignore ├── Procfile ├── README.md ├── package-lock.json ├── package.json └── src ├── bot.js ├── commands ├── fun │ └── roll.js ├── mod │ ├── ban.js │ ├── kick.js │ ├── lock.js │ ├── mute.js │ ├── prune.js │ └── unmute.js ├── rolereactions │ ├── addreactions.js │ └── editreaction.js └── roles │ ├── addrole.js │ └── delrole.js ├── database ├── database.js └── models │ └── message.js ├── events ├── members │ └── guildMemberAdd.js ├── message │ └── message.js ├── misc │ └── ready.js └── reactions │ ├── messageReactionAdd.js │ └── messageReactionRemove.js └── utils ├── dicefn.js ├── registry.js ├── tableConfig.js └── validate.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: node ./src/bot.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuyy/Discord.JS-V12-Tutorials/b753146a5795d1e7dca20ed1a789bd9f4a034200/README.md -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v12-discordjs-bot", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@discordjs/collection": { 8 | "version": "0.1.5", 9 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.5.tgz", 10 | "integrity": "sha512-CU1q0UXQUpFNzNB7gufgoisDHP7n+T3tkqTsp3MNUkVJ5+hS3BCvME8uCXAUFlz+6T2FbTCu75A+yQ7HMKqRKw==" 11 | }, 12 | "abort-controller": { 13 | "version": "3.0.0", 14 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 15 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 16 | "requires": { 17 | "event-target-shim": "^5.0.0" 18 | } 19 | }, 20 | "ajv": { 21 | "version": "6.12.0", 22 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", 23 | "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", 24 | "requires": { 25 | "fast-deep-equal": "^3.1.1", 26 | "fast-json-stable-stringify": "^2.0.0", 27 | "json-schema-traverse": "^0.4.1", 28 | "uri-js": "^4.2.2" 29 | } 30 | }, 31 | "ansi-colors": { 32 | "version": "4.1.1", 33 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 34 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" 35 | }, 36 | "ansi-regex": { 37 | "version": "4.1.0", 38 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 39 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" 40 | }, 41 | "ansi-styles": { 42 | "version": "3.2.1", 43 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 44 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 45 | "requires": { 46 | "color-convert": "^1.9.0" 47 | } 48 | }, 49 | "astral-regex": { 50 | "version": "1.0.0", 51 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", 52 | "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" 53 | }, 54 | "asynckit": { 55 | "version": "0.4.0", 56 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 57 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 58 | }, 59 | "bl": { 60 | "version": "2.2.0", 61 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", 62 | "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", 63 | "requires": { 64 | "readable-stream": "^2.3.5", 65 | "safe-buffer": "^5.1.1" 66 | } 67 | }, 68 | "bluebird": { 69 | "version": "3.5.1", 70 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 71 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" 72 | }, 73 | "bson": { 74 | "version": "1.1.3", 75 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz", 76 | "integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg==" 77 | }, 78 | "color-convert": { 79 | "version": "1.9.3", 80 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 81 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 82 | "requires": { 83 | "color-name": "1.1.3" 84 | } 85 | }, 86 | "color-name": { 87 | "version": "1.1.3", 88 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 89 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 90 | }, 91 | "combined-stream": { 92 | "version": "1.0.8", 93 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 94 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 95 | "requires": { 96 | "delayed-stream": "~1.0.0" 97 | } 98 | }, 99 | "core-util-is": { 100 | "version": "1.0.2", 101 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 102 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 103 | }, 104 | "debug": { 105 | "version": "3.1.0", 106 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 107 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 108 | "requires": { 109 | "ms": "2.0.0" 110 | }, 111 | "dependencies": { 112 | "ms": { 113 | "version": "2.0.0", 114 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 115 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 116 | } 117 | } 118 | }, 119 | "delayed-stream": { 120 | "version": "1.0.0", 121 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 122 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 123 | }, 124 | "denque": { 125 | "version": "1.4.1", 126 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", 127 | "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" 128 | }, 129 | "discord.js": { 130 | "version": "12.0.2", 131 | "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.0.2.tgz", 132 | "integrity": "sha512-iZiEA4Y61gqq/EjFfLXnkRK9pLapnax/vTVDUhs/mAhyqozAy0GOlk/MZI9rSa1iIoKTWRq6P9CRKhLNT2wUnA==", 133 | "requires": { 134 | "@discordjs/collection": "^0.1.5", 135 | "abort-controller": "^3.0.0", 136 | "form-data": "^3.0.0", 137 | "node-fetch": "^2.6.0", 138 | "prism-media": "^1.2.0", 139 | "setimmediate": "^1.0.5", 140 | "tweetnacl": "^1.0.3", 141 | "ws": "^7.2.1" 142 | } 143 | }, 144 | "dotenv": { 145 | "version": "8.2.0", 146 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", 147 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" 148 | }, 149 | "emoji-regex": { 150 | "version": "7.0.3", 151 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 152 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" 153 | }, 154 | "event-target-shim": { 155 | "version": "5.0.1", 156 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 157 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" 158 | }, 159 | "fast-deep-equal": { 160 | "version": "3.1.1", 161 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", 162 | "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" 163 | }, 164 | "fast-json-stable-stringify": { 165 | "version": "2.1.0", 166 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 167 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" 168 | }, 169 | "form-data": { 170 | "version": "3.0.0", 171 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", 172 | "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", 173 | "requires": { 174 | "asynckit": "^0.4.0", 175 | "combined-stream": "^1.0.8", 176 | "mime-types": "^2.1.12" 177 | } 178 | }, 179 | "inherits": { 180 | "version": "2.0.4", 181 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 182 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 183 | }, 184 | "is-fullwidth-code-point": { 185 | "version": "2.0.0", 186 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 187 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 188 | }, 189 | "isarray": { 190 | "version": "1.0.0", 191 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 192 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 193 | }, 194 | "json-schema-traverse": { 195 | "version": "0.4.1", 196 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 197 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 198 | }, 199 | "kareem": { 200 | "version": "2.3.1", 201 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", 202 | "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" 203 | }, 204 | "lodash": { 205 | "version": "4.17.15", 206 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 207 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 208 | }, 209 | "memory-pager": { 210 | "version": "1.5.0", 211 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 212 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 213 | "optional": true 214 | }, 215 | "mime-db": { 216 | "version": "1.43.0", 217 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", 218 | "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" 219 | }, 220 | "mime-types": { 221 | "version": "2.1.26", 222 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", 223 | "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", 224 | "requires": { 225 | "mime-db": "1.43.0" 226 | } 227 | }, 228 | "mongodb": { 229 | "version": "3.5.5", 230 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.5.tgz", 231 | "integrity": "sha512-GCjDxR3UOltDq00Zcpzql6dQo1sVry60OXJY3TDmFc2SWFY6c8Gn1Ardidc5jDirvJrx2GC3knGOImKphbSL3A==", 232 | "requires": { 233 | "bl": "^2.2.0", 234 | "bson": "^1.1.1", 235 | "denque": "^1.4.1", 236 | "require_optional": "^1.0.1", 237 | "safe-buffer": "^5.1.2", 238 | "saslprep": "^1.0.0" 239 | } 240 | }, 241 | "mongoose": { 242 | "version": "5.9.5", 243 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.5.tgz", 244 | "integrity": "sha512-2kMNZCZRWCMtww4f//CwdGH6BjO3+9/c3YdsC6nbzdJVyl8+GRtNfgrKUge3226VZXXLJa6LwxXN2K8/Dh4irg==", 245 | "requires": { 246 | "bson": "~1.1.1", 247 | "kareem": "2.3.1", 248 | "mongodb": "3.5.5", 249 | "mongoose-legacy-pluralize": "1.0.2", 250 | "mpath": "0.6.0", 251 | "mquery": "3.2.2", 252 | "ms": "2.1.2", 253 | "regexp-clone": "1.0.0", 254 | "safe-buffer": "5.1.2", 255 | "sift": "7.0.1", 256 | "sliced": "1.0.1" 257 | } 258 | }, 259 | "mongoose-legacy-pluralize": { 260 | "version": "1.0.2", 261 | "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", 262 | "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" 263 | }, 264 | "mpath": { 265 | "version": "0.6.0", 266 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.6.0.tgz", 267 | "integrity": "sha512-i75qh79MJ5Xo/sbhxrDrPSEG0H/mr1kcZXJ8dH6URU5jD/knFxCVqVC/gVSW7GIXL/9hHWlT9haLbCXWOll3qw==" 268 | }, 269 | "mquery": { 270 | "version": "3.2.2", 271 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", 272 | "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", 273 | "requires": { 274 | "bluebird": "3.5.1", 275 | "debug": "3.1.0", 276 | "regexp-clone": "^1.0.0", 277 | "safe-buffer": "5.1.2", 278 | "sliced": "1.0.1" 279 | } 280 | }, 281 | "ms": { 282 | "version": "2.1.2", 283 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 284 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 285 | }, 286 | "node-fetch": { 287 | "version": "2.6.0", 288 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", 289 | "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" 290 | }, 291 | "prism-media": { 292 | "version": "1.2.1", 293 | "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.1.tgz", 294 | "integrity": "sha512-R3EbKwJiYlTvGwcG1DpUt+06DsxOGS5W4AMEHT7oVOjG93MjpdhGX1whHyjnqknylLMupKAsKMEXcTNRbPe6Vw==" 295 | }, 296 | "process-nextick-args": { 297 | "version": "2.0.1", 298 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 299 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 300 | }, 301 | "punycode": { 302 | "version": "2.1.1", 303 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 304 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 305 | }, 306 | "readable-stream": { 307 | "version": "2.3.7", 308 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 309 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 310 | "requires": { 311 | "core-util-is": "~1.0.0", 312 | "inherits": "~2.0.3", 313 | "isarray": "~1.0.0", 314 | "process-nextick-args": "~2.0.0", 315 | "safe-buffer": "~5.1.1", 316 | "string_decoder": "~1.1.1", 317 | "util-deprecate": "~1.0.1" 318 | } 319 | }, 320 | "regexp-clone": { 321 | "version": "1.0.0", 322 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", 323 | "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" 324 | }, 325 | "require_optional": { 326 | "version": "1.0.1", 327 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 328 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 329 | "requires": { 330 | "resolve-from": "^2.0.0", 331 | "semver": "^5.1.0" 332 | } 333 | }, 334 | "resolve-from": { 335 | "version": "2.0.0", 336 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 337 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 338 | }, 339 | "safe-buffer": { 340 | "version": "5.1.2", 341 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 342 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 343 | }, 344 | "saslprep": { 345 | "version": "1.0.3", 346 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 347 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 348 | "optional": true, 349 | "requires": { 350 | "sparse-bitfield": "^3.0.3" 351 | } 352 | }, 353 | "semver": { 354 | "version": "5.7.1", 355 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 356 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 357 | }, 358 | "setimmediate": { 359 | "version": "1.0.5", 360 | "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", 361 | "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" 362 | }, 363 | "sift": { 364 | "version": "7.0.1", 365 | "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", 366 | "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" 367 | }, 368 | "slice-ansi": { 369 | "version": "2.1.0", 370 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", 371 | "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", 372 | "requires": { 373 | "ansi-styles": "^3.2.0", 374 | "astral-regex": "^1.0.0", 375 | "is-fullwidth-code-point": "^2.0.0" 376 | } 377 | }, 378 | "sliced": { 379 | "version": "1.0.1", 380 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 381 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" 382 | }, 383 | "sparse-bitfield": { 384 | "version": "3.0.3", 385 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 386 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 387 | "optional": true, 388 | "requires": { 389 | "memory-pager": "^1.0.2" 390 | } 391 | }, 392 | "string-width": { 393 | "version": "3.1.0", 394 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 395 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 396 | "requires": { 397 | "emoji-regex": "^7.0.1", 398 | "is-fullwidth-code-point": "^2.0.0", 399 | "strip-ansi": "^5.1.0" 400 | } 401 | }, 402 | "string_decoder": { 403 | "version": "1.1.1", 404 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 405 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 406 | "requires": { 407 | "safe-buffer": "~5.1.0" 408 | } 409 | }, 410 | "strip-ansi": { 411 | "version": "5.2.0", 412 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 413 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 414 | "requires": { 415 | "ansi-regex": "^4.1.0" 416 | } 417 | }, 418 | "table": { 419 | "version": "5.4.6", 420 | "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", 421 | "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", 422 | "requires": { 423 | "ajv": "^6.10.2", 424 | "lodash": "^4.17.14", 425 | "slice-ansi": "^2.1.0", 426 | "string-width": "^3.0.0" 427 | } 428 | }, 429 | "tweetnacl": { 430 | "version": "1.0.3", 431 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", 432 | "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" 433 | }, 434 | "uri-js": { 435 | "version": "4.2.2", 436 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 437 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 438 | "requires": { 439 | "punycode": "^2.1.0" 440 | } 441 | }, 442 | "util-deprecate": { 443 | "version": "1.0.2", 444 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 445 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 446 | }, 447 | "ws": { 448 | "version": "7.2.3", 449 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz", 450 | "integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==" 451 | } 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v12-discordjs-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "bot.js", 6 | "scripts": { 7 | "start" : "node ./src/bot.js", 8 | "dev" : "nodemon ./src/bot.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "ansi-colors": "^4.1.1", 15 | "discord.js": "^12.0.2", 16 | "dotenv": "^8.2.0", 17 | "mongoose": "^5.9.5", 18 | "table": "^5.4.6" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/bot.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const discord = require('discord.js'); 3 | const client = new discord.Client({ partials: ['MESSAGE', 'REACTION']}); 4 | const { registerCommands, registerEvents } = require('./utils/registry'); 5 | (async () => { 6 | client.login(process.env.BOT_TOKEN); 7 | client.commands = new Map(); 8 | client.cachedMessageReactions = new Map(); 9 | await registerEvents(client, '../events'); 10 | await registerCommands(client, '../commands'); 11 | 12 | })(); -------------------------------------------------------------------------------- /src/commands/fun/roll.js: -------------------------------------------------------------------------------- 1 | const { rollDice } = require('../../utils/dicefn'); 2 | 3 | module.exports = { 4 | run: async(client, message) => { 5 | message.reply("rolled a " + rollDice()); 6 | }, 7 | aliases: ['dice', 'rolldice'], 8 | description: 'Rolls the dice' 9 | } -------------------------------------------------------------------------------- /src/commands/mod/ban.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | run: async(client, message, args) => { 3 | if(!message.member.hasPermission('BAN_MEMBERS')) { 4 | message.channel.send("You don't have permission to use that command."); 5 | } 6 | else { 7 | try { 8 | let bannedMember = await message.guild.members.ban(args); 9 | if(bannedMember) 10 | console.log(bannedMember.tag + " was banned."); 11 | } 12 | catch(err) { 13 | console.log(err); 14 | } 15 | } 16 | }, 17 | aliases: [], 18 | description: 'Bans a guild member by their ID' 19 | } -------------------------------------------------------------------------------- /src/commands/mod/kick.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | run: async(client, message, args) => { 3 | if(!message.member.hasPermission('KICK_MEMBERS')) 4 | message.channel.send("You don't have permission to use that command."); 5 | else { 6 | let member = message.guild.members.cache.get(args); 7 | if(member) { 8 | try { 9 | await member.kick(); 10 | console.log('A member was kicked.'); 11 | } 12 | catch(err) { 13 | console.log(err); 14 | } 15 | } 16 | } 17 | }, 18 | aliases: [], 19 | description: 'Kicks a user' 20 | } -------------------------------------------------------------------------------- /src/commands/mod/lock.js: -------------------------------------------------------------------------------- 1 | const validateFlag = f => f === 'true' || f === 'false' || f === 'null'; 2 | const IGNORED = new Set([ 3 | // PLACE YOUR CHANNEL IDS HERE 4 | ]); 5 | 6 | module.exports = { 7 | run: async(client, message, args) => { 8 | if(args.split(' ').length !== 2) 9 | return message.channel.send('?lock TRUE | FALSE | NULL'); 10 | let [ roleId, flag ] = args.split(' '); 11 | if(!isNaN(roleId) && validateFlag(flag.toLowerCase())) { 12 | if(message.guild.roles.cache.has(roleId)) { 13 | flag = flag.toLowerCase() === 'true' ? true : (flag.toLowerCase() === 'false' ? false : null); 14 | const channels = message.guild.channels.cache.filter(ch => ch.type !== 'category'); 15 | channels.forEach(channel => { 16 | if(!IGNORED.has(channel.id)) { 17 | channel.updateOverwrite(roleId, { 18 | SEND_MESSAGES: !flag 19 | }).then(g => { 20 | console.log(`Updated ${g.name} (${g.id})`); 21 | if(flag) { 22 | if(!g.name.endsWith('🔒')) { 23 | g.edit({ name: g.name + ' 🔒'}); 24 | } 25 | } else { 26 | g.edit({ name: g.name.replace(/\s*🔒/, '')}); 27 | } 28 | }) 29 | .catch(err => console.log(err)); 30 | } else { 31 | console.log(`Skipping ${channel.name} (${channel.id})`); 32 | } 33 | }); 34 | } 35 | else { 36 | message.channel.send('Invalid Role.'); 37 | } 38 | } 39 | }, 40 | aliases: ['lockdown'], 41 | description: 'locksdown a channel' 42 | } -------------------------------------------------------------------------------- /src/commands/mod/mute.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | run: async(client, message, args) => { 3 | if(!message.member.hasPermission(['KICK_MEMBERS', 'BAN_MEMBERS'])) 4 | message.channel.send("You don't have permissions to use that command."); 5 | else { 6 | let memberId = message.content.substring(message.content.indexOf(' ')+1); 7 | let member = message.guild.members.cache.get(args); 8 | if(member) { 9 | if(member.hasPermission(['KICK_MEMBERS', 'BAN_MEMBERS']) && !message.member.hasPermission('ADMINISTRATOR')) 10 | message.channel.send("You cannot mute that person!"); 11 | else { 12 | let mutedRole = message.guild.roles.cache.get('690056380140355621'); 13 | if(mutedRole) { 14 | member.roles.add(mutedRole); 15 | message.channel.send("User was muted."); 16 | } 17 | else 18 | message.channel.send("Muted role not found."); 19 | } 20 | } 21 | else 22 | message.channel.send("Member not found."); 23 | } 24 | }, 25 | aliases: [], 26 | description: 'Mutes a user' 27 | } -------------------------------------------------------------------------------- /src/commands/mod/prune.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | run: async(client, msg, args) => { 3 | let [ userId, limit ] = args.split(/\s+/); 4 | if(!userId && !limit) { 5 | let deletedMessages = await msg.channel.bulkDelete(); 6 | msg.channel.send(`${deletedMessages.size} messages were deleted.`); 7 | } 8 | if(!userId || !limit) return msg.channel.send('Please provide the correct arguments.'); 9 | let r = new RegExp(/^\d+$/); 10 | if(!r.test(userId)) return msg.channel.send('Please provide a valid user id.'); 11 | if(isNaN(limit)) return msg.channel.send('Please provide a numeric value for limit'); 12 | if(limit > 100) return msg.channel.send('Limit must be less than or equal to 100.'); 13 | try { 14 | let fetchedMessages = await msg.channel.messages.fetch({ limit }); 15 | let filteredMessages = fetchedMessages.filter(msg => msg.author.id === userId); 16 | let deletedMessages = await msg.channel.bulkDelete(filteredMessages); 17 | msg.channel.send(`${deletedMessages.size} messages were deleted.`); 18 | } 19 | catch(err) { 20 | console.log(err); 21 | } 22 | }, 23 | aliases: ['purge'], 24 | description: 'Deletes a number of messages from a user in a channel.' 25 | } -------------------------------------------------------------------------------- /src/commands/mod/unmute.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | run: async(client, message, args) => { 3 | if(!message.member.hasPermission(['KICK_MEMBERS', 'BAN_MEMBERS'])) { 4 | message.channel.send("You don't have permissions to use that command."); 5 | } 6 | else { 7 | let memberId = message.content.substring(message.content.indexOf(' ')+1); 8 | let member = message.guild.members.cache.get(args); 9 | if(member) { 10 | if(member.hasPermission(['KICK_MEMBERS', 'BAN_MEMBERS']) && !message.member.hasPermission('ADMINISTRATOR')) { 11 | message.channel.send("You cannot unmute that person!"); 12 | } 13 | else { 14 | let mutedRole = message.guild.roles.cache.get('690056380140355621'); 15 | if(mutedRole) { 16 | member.roles.remove(mutedRole); 17 | message.channel.send("User was unmuted."); 18 | } 19 | else { 20 | message.channel.send("Muted role not found."); 21 | } 22 | } 23 | } 24 | else { 25 | message.channel.send("Member not found."); 26 | } 27 | } 28 | }, 29 | aliases: [], 30 | description: 'Unmutes a user' 31 | } -------------------------------------------------------------------------------- /src/commands/rolereactions/addreactions.js: -------------------------------------------------------------------------------- 1 | const { MessageCollector } = require('discord.js'); 2 | const MessageModel = require('../../database/models/message'); 3 | 4 | let msgCollectorFilter = (newMsg, originalMsg) => newMsg.author.id === originalMsg.author.id; 5 | module.exports = { 6 | run: async(client, message, args) => { 7 | if(args.split(/\s+/).length !== 1) { 8 | let msg = await message.channel.send("Too many arguments. Must only provide 1 message id"); 9 | await msg.delete({ timeout: 3500 }).catch(err => console.log(err)); 10 | } 11 | else { 12 | try { 13 | let fetchedMessage = await message.channel.messages.fetch(args); 14 | if(fetchedMessage) { 15 | await message.channel.send("Please provide all of the emoji names with the role name, one by one, separated with a comma.\ne.g: snapchat, snapchat, where the emoji name comes first, role name comes second."); 16 | let collector = new MessageCollector(message.channel, msgCollectorFilter.bind(null, message)); 17 | let emojiRoleMappings = new Map(); 18 | collector.on('collect', msg => { 19 | let { cache } = msg.guild.emojis; 20 | if(msg.content.toLowerCase() === '?done') { 21 | collector.stop('done command was issued.'); 22 | return; 23 | } 24 | let [ emojiName, roleName ] = msg.content.split(/,\s+/); 25 | if(!emojiName && !roleName) return; 26 | let emoji = cache.find(emoji => emoji.name.toLowerCase() === emojiName.toLowerCase()); 27 | if(!emoji) { 28 | msg.channel.send("Emoji does not exist. Try again.") 29 | .then(msg => msg.delete({ timeout: 2000 })) 30 | .catch(err => console.log(err)); 31 | return; 32 | } 33 | let role = msg.guild.roles.cache.find(role => role.name.toLowerCase() === roleName.toLowerCase()); 34 | if(!role) { 35 | msg.channel.send("Role does not exist. Try again.") 36 | .then(msg => msg.delete({ timeout: 2000 })) 37 | .catch(err => console.log(err)); 38 | return; 39 | } 40 | fetchedMessage.react(emoji) 41 | .then(emoji => console.log("Reacted.")) 42 | .catch(err => console.log(err)); 43 | emojiRoleMappings.set(emoji.id, role.id); 44 | }); 45 | collector.on('end', async (collected, reason) => { 46 | let findMsgDocument = await MessageModel 47 | .findOne({ messageId: fetchedMessage.id }) 48 | .catch(err => console.log(err)); 49 | if(findMsgDocument) { 50 | console.log("The message exists.. Don't save..."); 51 | message.channel.send("A role reaction set up exists for this message already..."); 52 | } 53 | else { 54 | let dbMsgModel = new MessageModel({ 55 | messageId: fetchedMessage.id, 56 | emojiRoleMappings: emojiRoleMappings 57 | }); 58 | dbMsgModel.save() 59 | .then(m => console.log(m)) 60 | .catch(err => console.log(err)); 61 | } 62 | }); 63 | } 64 | } 65 | catch(err) { 66 | console.log(err); 67 | let msg = await message.channel.send("Invalid id. Message was not found."); 68 | await msg.delete({ timeout: 3500 }).catch(err => console.log(err)); 69 | } 70 | } 71 | }, 72 | aliases: [], 73 | description: 'Enables a message to listen to reactions to give roles.' 74 | } -------------------------------------------------------------------------------- /src/commands/rolereactions/editreaction.js: -------------------------------------------------------------------------------- 1 | const MessageModel = require('../../database/models/message'); 2 | const { MessageCollector } = require('discord.js'); 3 | module.exports = { 4 | run: async(client, message, args) => { 5 | if(args.split(" ").length !== 1) return; 6 | // Check if the message exists. 7 | const { channel, author } = message; 8 | try { 9 | let fetchedMessage = channel.messages.cache.get(args) || await channel.messages.fetch(args); 10 | if(!fetchedMessage) 11 | channel.send("Message not found."); 12 | else { 13 | // Check if the message exists in the DB. 14 | let msgModel = await MessageModel.findOne({ messageId: args }); 15 | if(msgModel) { 16 | client.emit('msgDocFetched', msgModel); 17 | // Prompt the user for configurations. 18 | let filter = m => m.author.id === author.id && (m.content.toLowerCase() === 'add' || m.content.toLowerCase() === 'remove'); 19 | let tempMsg = channel.send("Do you want to add or remove from the reaction configuration? Type add or remove"); 20 | try { 21 | let awaitMsgOps = { max: 1, time: 4000, errors: ['time'] }; 22 | let choice = (await channel.awaitMessages(filter, awaitMsgOps)).first(); 23 | if(choice.content === "add") { 24 | let addMsgPrompt = await channel.send("Enter an emoji name followed by the corresponding role name, separated with a comma. e.g: some_emoji, some_role"); 25 | let collectorResult = await handleCollector(fetchedMessage, author, channel, msgModel, args); 26 | console.log(collectorResult); 27 | } 28 | else { 29 | 30 | } 31 | } 32 | catch(err) { 33 | console.log(err); 34 | } 35 | } 36 | else { 37 | message.channel.send("There is no configuration for that message. Please use ?addreactions on a message to set up Role Reactions on that message.") 38 | } 39 | } 40 | } 41 | catch(err) { 42 | console.log(err); 43 | } 44 | }, 45 | aliases: [], 46 | description: 'Edits the role reaction configuration' 47 | } 48 | function handleCollector(fetchedMessage, author, channel, msgModel, messageId) { 49 | return new Promise((resolve, reject) => { 50 | let collectorFilter = (m) => m.author.id === author.id; 51 | let collector = new MessageCollector(channel, collectorFilter); 52 | let emojiRoleMappings = new Map(Object.entries(msgModel.emojiRoleMappings)); 53 | collector.on('collect', msg => { 54 | if(msg.content.toLowerCase() === '?done') { 55 | collector.stop(); 56 | resolve(); 57 | } 58 | else { 59 | let { cache } = msg.guild.emojis; 60 | let [ emojiName, roleName ] = msg.content.split(/,\s+/); 61 | if(!emojiName && !roleName) return; 62 | let emoji = cache.find(emoji => emoji.name.toLowerCase() === emojiName.toLowerCase()); 63 | if(!emoji) { 64 | msg.channel.send("Emoji does not exist. Try again.") 65 | .then(msg => msg.delete({ timeout: 2000 })) 66 | .catch(err => console.log(err)); 67 | return; 68 | } 69 | let role = msg.guild.roles.cache.find(role => role.name.toLowerCase() === roleName.toLowerCase()); 70 | if(!role) { 71 | msg.channel.send("Role does not exist. Try again.") 72 | .then(msg => msg.delete({ timeout: 2000 })) 73 | .catch(err => console.log(err)); 74 | return; 75 | } 76 | fetchedMessage.react(emoji) 77 | .then(emoji => console.log("Reacted.")) 78 | .catch(err => console.log(err)); 79 | emojiRoleMappings.set(emoji.id, role.id); 80 | } 81 | }); 82 | collector.on('end', () => { 83 | console.log("Done..."); 84 | resolve(emojiRoleMappings); 85 | }); 86 | }); 87 | } -------------------------------------------------------------------------------- /src/commands/roles/addrole.js: -------------------------------------------------------------------------------- 1 | const checkPermissionRole = (role) => role.permissions.has('ADMINISTRATOR') || role.permissions.has('KICK_MEMBERS') || role.permissions.has('BAN_MEMBERS') || role.permissions.has('MANAGE_GUILD') || role.permissions.has('MANAGE_CHANNELS'); 2 | 3 | module.exports = { 4 | run: async(client, message, args) => { 5 | let roleNames = args.split(", "); 6 | let roleSet = new Set(roleNames); 7 | let { cache } = message.guild.roles; 8 | roleSet.forEach(roleName => { 9 | let role = cache.find(role => role.name.toLowerCase() === roleName.toLowerCase()); 10 | if(role) { 11 | if(message.member.roles.cache.has(role.id)) { 12 | message.channel.send("You already have this role!"); 13 | return; 14 | } 15 | if(checkPermissionRole(role)) { 16 | message.channel.send("You cannot add yourself to this role."); 17 | } 18 | else { 19 | message.member.roles.add(role) 20 | .then(member => message.channel.send("You were added to this role!")) 21 | .catch(err => { 22 | console.log(err); 23 | message.channel.send("Something went wrong..."); 24 | }); 25 | } 26 | } 27 | else { 28 | message.channel.send("Role not found!"); 29 | } 30 | }); 31 | }, 32 | aliases: ['roleadd'], 33 | description: 'Adds a role to a Guild Member' 34 | } -------------------------------------------------------------------------------- /src/commands/roles/delrole.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | run: async(client, message, args) => { 3 | let roleNames = args.split(", "); 4 | let roleSet = new Set(roleNames); 5 | let { cache } = message.guild.roles; 6 | roleSet.forEach(roleName => { 7 | let role = cache.find(role => role.name.toLowerCase() === roleName.toLowerCase()); 8 | if(role) { 9 | if(message.member.roles.cache.has(role.id)) { 10 | message.member.roles.remove(role) 11 | .then(member => message.channel.send("You were removed to this role!")) 12 | .catch(err => { 13 | console.log(err); 14 | message.channel.send("Something went wrong..."); 15 | }); 16 | } 17 | } 18 | else { 19 | message.channel.send("Role not found!"); 20 | } 21 | }); 22 | }, 23 | aliases: ['deleterole', 'roledelete'], 24 | description: 'Deletes a role from a Guild Member' 25 | } -------------------------------------------------------------------------------- /src/database/database.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | module.exports = mongoose.connect('mongodb://localhost:27017/RoleReaction', { useNewUrlParser: true, useUnifiedTopology: true}); -------------------------------------------------------------------------------- /src/database/models/message.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const MessageSchema = new mongoose.Schema({ 3 | messageId: { type: String, required: true }, 4 | emojiRoleMappings: { type: mongoose.Schema.Types.Mixed } 5 | }); 6 | 7 | const MessageModel = module.exports = mongoose.model('message', MessageSchema); -------------------------------------------------------------------------------- /src/events/members/guildMemberAdd.js: -------------------------------------------------------------------------------- 1 | module.exports = (client, member) => { 2 | console.log('Guild member add was registered.'); 3 | }; -------------------------------------------------------------------------------- /src/events/message/message.js: -------------------------------------------------------------------------------- 1 | const PREFIX = process.env.PREFIX; 2 | module.exports = (client, message) => { 3 | if(message.author.bot) return; 4 | if(!message.content.startsWith(PREFIX)) return; 5 | let cmdName = message.content.substring(message.content.indexOf(PREFIX)+1).split(new RegExp(/\s+/)).shift(); 6 | let argsToParse = message.content.substring(message.content.indexOf(' ')+1); 7 | if(client.commands.get(cmdName)) 8 | client.commands.get(cmdName)(client, message, argsToParse); 9 | else 10 | console.log("Command does not exist."); 11 | }; -------------------------------------------------------------------------------- /src/events/misc/ready.js: -------------------------------------------------------------------------------- 1 | const { createStream } = require('table'); 2 | const tableConfig = require('../../utils/tableConfig'); 3 | const { commandStatus, eventStatus } = require('../../utils/registry'); 4 | 5 | module.exports = async (client) => { 6 | console.log(`${client.user.tag} has logged in.`); 7 | await loadTable(commandStatus, 50); 8 | console.log("\n"); 9 | await loadTable(eventStatus, 50); 10 | // database.then(() => console.log("Connected to DB.")).catch(err => console.log(err)); 11 | } 12 | function loadTable(arr, interval) { 13 | let fn, i = 0, stream = createStream(tableConfig); 14 | return new Promise((resolve, reject) => { 15 | fn = setInterval(() => { 16 | if(i === arr.length) 17 | { 18 | clearInterval(fn); 19 | resolve(); 20 | } 21 | else { 22 | stream.write(arr[i]); 23 | i++; 24 | } 25 | }, interval); 26 | }) 27 | } -------------------------------------------------------------------------------- /src/events/reactions/messageReactionAdd.js: -------------------------------------------------------------------------------- 1 | const MessageModel = require('../../database/models/message'); 2 | 3 | module.exports = async (client, reaction, user) => { 4 | let addMemberRole = (emojiRoleMappings) => { 5 | if(emojiRoleMappings.hasOwnProperty(reaction.emoji.id)) { 6 | let roleId = emojiRoleMappings[reaction.emoji.id]; 7 | let role = reaction.message.guild.roles.cache.get(roleId); 8 | let member = reaction.message.guild.members.cache.get(user.id); 9 | if(role && member) { 10 | member.roles.add(role); 11 | } 12 | } 13 | } 14 | if(reaction.message.partial) { 15 | await reaction.message.fetch(); 16 | let { id } = reaction.message; 17 | try { 18 | let msgDocument = await MessageModel.findOne({ messageId: id }); 19 | if(msgDocument) { 20 | client.cachedMessageReactions.set(id, msgDocument.emojiRoleMappings); 21 | let { emojiRoleMappings } = msgDocument; 22 | addMemberRole(emojiRoleMappings); 23 | } 24 | } 25 | catch(err) { 26 | console.log(err); 27 | } 28 | } 29 | else { 30 | let emojiRoleMappings = client.cachedMessageReactions.get(reaction.message.id); 31 | addMemberRole(emojiRoleMappings); 32 | } 33 | } -------------------------------------------------------------------------------- /src/events/reactions/messageReactionRemove.js: -------------------------------------------------------------------------------- 1 | const cachedMessageReactions = new Map(); 2 | const MessageModel = require('../../database/models/message'); 3 | 4 | module.exports = async (client, reaction, user) => { 5 | let removeMemberRole = (emojiRoleMappings) => { 6 | if(emojiRoleMappings.hasOwnProperty(reaction.emoji.id)) { 7 | let roleId = emojiRoleMappings[reaction.emoji.id]; 8 | let role = reaction.message.guild.roles.cache.get(roleId); 9 | let member = reaction.message.guild.members.cache.get(user.id); 10 | if(role && member) { 11 | member.roles.remove(role); 12 | } 13 | } 14 | } 15 | if(reaction.message.partial) { 16 | await reaction.message.fetch(); 17 | let { id } = reaction.message; 18 | try { 19 | let msgDocument = await MessageModel.findOne({ messageId: id }); 20 | if(msgDocument) { 21 | client.cachedMessageReactions.set(id, msgDocument.emojiRoleMappings); 22 | let { emojiRoleMappings } = msgDocument; 23 | removeMemberRole(emojiRoleMappings); 24 | } 25 | } 26 | catch(err) { 27 | console.log(err); 28 | } 29 | } 30 | else { 31 | let emojiRoleMappings = client.cachedMessageReactions.get(reaction.message.id); 32 | removeMemberRole(emojiRoleMappings); 33 | } 34 | } -------------------------------------------------------------------------------- /src/utils/dicefn.js: -------------------------------------------------------------------------------- 1 | const rollDice = () => Math.floor(Math.random() * 6) + 1; 2 | 3 | module.exports = { rollDice }; -------------------------------------------------------------------------------- /src/utils/registry.js: -------------------------------------------------------------------------------- 1 | const c = require('ansi-colors'); 2 | const fs = require('fs').promises; 3 | const path = require('path'); 4 | const { checkCommandModule, checkProperties } = require('./validate'); 5 | const commandStatus = [ 6 | [`${c.bold('Command')}`, `${c.bold('Status')}`, `${c.bold('Description')}`] 7 | ], eventStatus = [ 8 | [`${c.bold('Event')}`, `${c.bold('Status')}`, `${c.bold('Description')}`] 9 | ]; 10 | 11 | async function registerCommands(client, dir) { 12 | let files = await fs.readdir(path.join(__dirname, dir)); 13 | // Loop through each file. 14 | for(let file of files) { 15 | let stat = await fs.lstat(path.join(__dirname, dir, file)); 16 | if(stat.isDirectory()) // If file is a directory, recursive call recurDir 17 | registerCommands(client, path.join(dir, file)); 18 | else { 19 | // Check if file is a .js file. 20 | if(file.endsWith(".js")) { 21 | let cmdName = file.substring(0, file.indexOf(".js")); 22 | try { 23 | let cmdModule = require(path.join(__dirname, dir, file)); 24 | if(checkCommandModule(cmdName, cmdModule)) { 25 | if(checkProperties(cmdName, cmdModule)) { 26 | let { aliases } = cmdModule; 27 | client.commands.set(cmdName, cmdModule.run); 28 | if(aliases.length !== 0) 29 | aliases.forEach(alias => client.commands.set(alias, cmdModule.run)); 30 | commandStatus.push( 31 | [`${c.cyan(`${cmdName}`)}`, `${c.bgGreenBright('Success')}`, `${cmdModule.description}`] 32 | ) 33 | } 34 | } 35 | } 36 | catch(err) { 37 | console.log(err); 38 | commandStatus.push( 39 | [`${c.white(`${cmdName}`)}`, `${c.bgRedBright('Failed')}`, ''] 40 | ); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | 47 | async function registerEvents(client, dir) { 48 | let files = await fs.readdir(path.join(__dirname, dir)); 49 | // Loop through each file. 50 | for(let file of files) { 51 | let stat = await fs.lstat(path.join(__dirname, dir, file)); 52 | if(stat.isDirectory()) // If file is a directory, recursive call recurDir 53 | registerEvents(client, path.join(dir, file)); 54 | else { 55 | // Check if file is a .js file. 56 | if(file.endsWith(".js")) { 57 | let eventName = file.substring(0, file.indexOf(".js")); 58 | try { 59 | let eventModule = require(path.join(__dirname, dir, file)); 60 | client.on(eventName, eventModule.bind(null, client)); 61 | eventStatus.push( 62 | [`${c.cyan(`${eventName}`)}`, `${c.bgGreenBright('Success')}`, `${eventModule.description}`] 63 | ) 64 | } 65 | catch(err) { 66 | console.log(err); 67 | eventStatus.push( 68 | [`${c.white(`${eventName}`)}`, `${c.bgRedBright('Failed')}`, ''] 69 | ); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | module.exports = { 77 | commandStatus, 78 | eventStatus, 79 | registerEvents, 80 | registerCommands 81 | }; -------------------------------------------------------------------------------- /src/utils/tableConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | columnDefault: { 3 | width: 30 4 | }, 5 | columnCount: 3, 6 | border: { 7 | topBody: `─`, 8 | topJoin: `┬`, 9 | topLeft: `┌`, 10 | topRight: `┐`, 11 | 12 | bottomBody: `─`, 13 | bottomJoin: `┴`, 14 | bottomLeft: `└`, 15 | bottomRight: `┘`, 16 | 17 | bodyLeft: `│`, 18 | bodyRight: `│`, 19 | bodyJoin: `│`, 20 | 21 | joinBody: `─`, 22 | joinLeft: `├`, 23 | joinRight: `┤`, 24 | joinJoin: `┼` 25 | } 26 | } -------------------------------------------------------------------------------- /src/utils/validate.js: -------------------------------------------------------------------------------- 1 | module.exports.checkCommandModule = (cmdName, cmdModule) => { 2 | if(!cmdModule.hasOwnProperty('run')) 3 | throw new Error(`${cmdName} command module does not have property 'run'`); 4 | if(!cmdModule.hasOwnProperty('description')) 5 | throw new Error(`${cmdName} command module does not have property 'description`); 6 | if(!cmdModule.hasOwnProperty('aliases')) 7 | throw new Error(`${cmdNamd} command module does not have property 'aliases'`); 8 | return true; 9 | } 10 | module.exports.checkProperties = (cmdName, cmdModule) => { 11 | if(typeof cmdModule.run !== 'function') 12 | throw new Error(`${cmdName} command: run is not a function`); 13 | if(typeof cmdModule.description !== 'string') 14 | throw new Error(`${cmdName} command: description is not a string`); 15 | if(!Array.isArray(cmdModule.aliases)) 16 | throw new Error(`${cmdName} command: aliases is not an Array`); 17 | return true; 18 | } 19 | --------------------------------------------------------------------------------