diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index f67f1c2..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "env": { - "browser": true, - "node": true, - "commonjs": true, - "es6": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "sourceType": "module" - }, - "rules": { - "indent": [ - "warn", - "tab", - { "SwitchCase": 1} - ], - "quotes": [ - "warn", - "double" - ], - "semi": [ - "error", - "always" - ], - "no-undef": "error", - "no-unused-vars": "warn", - "eqeqeq": ["error", "always"] - } -} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 7836b18..0000000 --- a/.gitignore +++ /dev/null @@ -1,57 +0,0 @@ -### Discord bots #### -guilds.json -token.json -log - -### Node ### -# Logs -logs -*.log -npm-debug.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules -node_cache -jspm_packages - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - - -# End of https://www.gitignore.io/api/node \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 799e6bc..0000000 --- a/.npmrc +++ /dev/null @@ -1,3 +0,0 @@ -save=true -save-exact=true -cache=node_cache diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index fa3533d..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - // Use IntelliSense to learn about possible Node.js debug attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "program": "${workspaceRoot}/wrapper.js" - } - ] -} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 7f39e10..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,114 +0,0 @@ -# Changelog - -## v2.0.0-b1 - -### Added -- Multi-guild support -- In-chat commands for setup and configuration - - Add a new feed - - View a list of feeds - - Remove an existing feed - -### Updated - -- Make save file configurable to allow use as a module with other bots -- Update config file structure -- Now uses discord.js instead of discord.io -- YouTube links automatically handled; no more separate "YouTube mode" config item - -### Fixed - -- Crash if trying to view feeds list before any feeds have been set up - -## v1.4.0 - -### Added - -- Support for posting links from multiple feeds -- Tagging of separate roles for each feed being checked - -### Updated - -- Updated bot connection code to use my discord-bot-wrapper - -### Removed - -- !logsplease command removed as the OTT logging was just being annoying - -## v1.3.2 - -### Fixed - -- Fixed list posting channel messages being ignored - -## v1.3.1 - -### Fixed - -- Developer commands can now be used from any channel or PM - -## v1.3.0 - -### Added - -- Deletion of "You have successfully subscribed" messages after a short delay (configurable) -- 'Developer' commands that can only be accessed by specified users -- !cacheList developer command to view the cached URLs - -### Updated -- !logsplease is now a developer command -- Subscriptions are now done using a role - - !subscribe and !unsibscribe add and remove the user from the role - - !sublist command is now removed - - The role is mentioned when the link is posted, rather than a long chain of user IDs - -## v1.2.1 - -### Fixed - -- Fixed multiple users being unsubscribed when one user unsubscribes - -## v1.2.0 - -### Added - -- Chat message/command to request a list of subscribed users -- The ability for users to 'subscribe' so they are tagged whenever a new link is posted -- Logging to a file -- Ability for user to request an upload of the logs file - -### Updated - -- Added basic spam reduction when logging so the same message won't get logged multiple times in a row -- Refactored a bunch of code to improve efficiency -- Updated timer logic to only ever use a single timer, and share it between posting and reconnecting - -## v1.1.2 - -### Updated -- Updated reconnect logic to hopefully be more stable - -## v1.1.1 - -### Added -- Reconnect timer to repeatedly try reconnect at intervals - -### Updated - -- Updated support for https conversion to http to hopefully be more consistent - -## v1.1.0 - -### Added - -- Added togglable YouTube mode - - Converts full URLs to YouTube share URLs - - Checks against both YouTube full and share URLs to ensure same video not posted twice -- New logging class to handle logging - -### Updated -- Major refactor of a significant portion of the bot's code - should be easier to maintain now, but may have introduced some new bugs -- Changed expected name for bot config file to bot-config.json rather than botConfig.json - -### Fixed -- New timer being created every time the bot reconnected diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 46a410d..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 Benji Higgins - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 8b13789..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/config.json b/app/config.json deleted file mode 100644 index 1a8a1b0..0000000 --- a/app/config.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "feedCheckIntervalSec": 30, - "maxCacheSize": 10, - "generic": { - "saveFile": "./guilds.json", - "saveIntervalSec": 60, - "defaultDMResponse": "This bot does not have any handling for direct messages. To learn more or get help please visit http://benji7425.github.io, or join my Discord server here: https://discord.gg/SSkbwSJ" - }, - "commands": { - "admin": { - "version": "version", - "addFeed": "add-feed", - "removeFeed": "remove-feed", - "viewFeeds": "view-feeds" - }, - "anyone": {} - } -} \ No newline at end of file diff --git a/app/index.js b/app/index.js deleted file mode 100644 index df86906..0000000 --- a/app/index.js +++ /dev/null @@ -1,152 +0,0 @@ -//node imports -const FileSystem = require("fs"); - -//external lib imports -const JsonFile = require("jsonfile"); //for saving to/from JSON -const Url = require("url"); //for url parsing -const GetUrls = require("get-urls"); //for extracting urls from messages - -//my imports -const DiscordUtil = require("discordjs-util"); - -//app component imports -const GuildData = require("./models/guild-data.js"); -const FeedData = require("./models/feed-data.js"); - -//global vars -let writeFile = null; - -module.exports = (client, config = null) => { - config = config || require("./config.json"); //load config file - const guildsData = FileSystem.existsSync(config.generic.saveFile) ? fromJSON(JsonFile.readFileSync(config.generic.saveFile)) : {}; //read data from file, or generate new one if file doesn't exist - writeFile = () => JsonFile.writeFile(config.generic.saveFile, guildsData, err => { if (err) DiscordUtil.dateError("Error writing file", err); }); - setInterval(() => writeFile(), config.generic.saveIntervalSec * 1000); //set interval to save data to file - - parseLinksInGuilds(client.guilds, guildsData).then(() => writeFile(guildsData)) - .then(() => checkFeedsInGuilds(client.guilds, guildsData)) - .then(() => setInterval(() => checkFeedsInGuilds(client.guilds, guildsData), config.feedCheckIntervalSec * 1000)); //set up an interval to check all the feeds - - //set up an on message handler to detect when links are posted - client.on("message", message => { - if (message.author.id !== client.user.id) { //check the bot isn't triggering itself - if (message.channel.type === "dm") - HandleMessage.dm(client, config, message); - else if (message.channel.type === "text" && message.member) - HandleMessage.text(client, config, message, guildsData); - } - }); -}; - -const HandleMessage = { - dm: (client, config, message) => { - message.reply(config.generic.defaultDMResponse); - }, - text: (client, config, message, guildsData) => { - //handle admins invoking commands - if (message.content.startsWith(message.guild.me.toString()) //user is @mention-ing the bot - && message.member.permissions.has("ADMINISTRATOR")) //user has admin perms - { - const params = message.content.split(" "); //split the message at the spaces - - //check which command was invoked - switch (params[1]) { - case config.commands.admin.version: - message.reply("v" + require("../package.json").version); - break; - case config.commands.admin.addFeed: - addFeed(client, guildsData, message, config.maxCacheSize); - break; - case config.commands.admin.removeFeed: - removeFeed(client, guildsData, message); - break; - case config.commands.admin.viewFeeds: - viewFeeds(client, guildsData[message.guild.id], message); - break; - } - } - else if (guildsData[message.guild.id]) { - guildsData[message.guild.id].feeds.forEach(feedData => { - if (message.channel.name === feedData.channelName) - feedData.cachedLinks.push(...GetUrls(message.content)); //spread the urlSet returned by GetUrls into the cache array - }); - } - } -}; - -function addFeed(client, guildsData, message, maxCacheSize) { - const feedUrl = [...GetUrls(message.content)][0]; - const channel = message.mentions.channels.first(); - - if (!feedUrl || !channel) - return message.reply("Please provide both a channel and an RSS feed URL. You can optionally @mention a role also."); - - const role = message.mentions.roles.first(); - - const feedData = new FeedData({ - url: feedUrl, - channelName: channel.name, - roleName: role ? role.name : null, - maxCacheSize: maxCacheSize - }); - - //ask the user if they're happy with the details they set up, save if yes, don't if no - DiscordUtil.ask(client, message.channel, message.member, "Are you happy with this?\n" + feedData.toString()) - .then(responseMessage => { - - //if they responded yes, save the feed and let them know, else tell them to start again - if (responseMessage.content.toLowerCase() === "yes") { - if (!guildsData[message.guild.id]) - guildsData[message.guild.id] = new GuildData({ id: message.guild.id, feeds: [] }); - - guildsData[message.guild.id].feeds.push(feedData); - writeFile(guildsData); - responseMessage.reply("Your new feed has been saved!"); - } - else - responseMessage.reply("Your feed has not been saved, please add it again with the correct details"); - }); -} - -function removeFeed(client, guildsData, message) { - const parameters = message.content.split(" "); - if (parameters.length !== 3) - message.reply(`Please use the command as such:\n\`\`\` @${client.user.username} remove-feed feedid\`\`\``); - else { - const guildData = guildsData[message.guild.id]; - const idx = guildData.feeds.findIndex(feed => feed.id === parameters[2]); - if (!Number.isInteger(idx)) - message.reply("Can't find feed with id " + parameters[2]); - else { - guildData.feeds.splice(idx, 1); - writeFile(guildsData); - message.reply("Feed removed!"); - } - } -} - -function viewFeeds(client, guildData, message) { - if (!guildData) - return; - - message.reply(guildData.feeds.map(f => f.toString()).join("\n")); -} - -function checkFeedsInGuilds(guilds, guildsData) { - Object.keys(guildsData).forEach(key => guildsData[key].checkFeeds(guilds)); -} - -function parseLinksInGuilds(guilds, guildsData) { - const promises = []; - for (let guildId of guilds.keys()) { - const guildData = guildsData[guildId]; - if (guildData) - promises.push(guildData.cachePastPostedLinks(guilds.get(guildId))); - } - return Promise.all(promises); -} - -function fromJSON(json) { - const guildsData = Object.keys(json); - guildsData.forEach(guildID => { json[guildID] = new GuildData(json[guildID]); }); - return json; -} \ No newline at end of file diff --git a/app/models/feed-data.js b/app/models/feed-data.js deleted file mode 100644 index da5a90c..0000000 --- a/app/models/feed-data.js +++ /dev/null @@ -1,89 +0,0 @@ -//my imports -const DiscordUtil = require("discordjs-util"); - -//external lib imports -const Dns = require("dns"); //for host resolution checking -const Url = require("url"); //for url parsing -const FeedRead = require("feed-read"); //for extracing new links from RSS feeds -const GetUrls = require("get-urls"); //for extracting urls from messages -const ShortID = require("shortid"); //to provide ids for each feed, allowing guilds to remove them - -module.exports = class FeedData { - constructor({ id, url, channelName, roleName, cachedLinks, maxCacheSize }) { - this.id = id || ShortID.generate(); - this.url = url; - this.channelName = channelName; - this.roleName = roleName; - this.cachedLinks = cachedLinks || []; - this.maxCacheSize = maxCacheSize || 10; - - this.cachedLinks.push = (...elements) => { - const unique = elements - .map(el => normaliseUrl(el)) //normalise all the urls - .filter(el => !this.cachedLinks.includes(el)); //filter out any already cached - Array.prototype.push.apply(this.cachedLinks, unique); - - if (this.cachedLinks.length > this.maxCacheSize) - this.cachedLinks.splice(0, this.cachedLinks.length - this.maxCacheSize); //remove the # of elements above the max from the beginning - }; - } - - /** - * Returns a promise providing all the links posted in the last 100 messages - * @param {Discord.Guild} guild The guild this feed belongs to - * @returns {Promise} Links posted in last 100 messages - */ - updatePastPostedLinks(guild) { - const channel = guild.channels.find(ch => ch.type === "text" && ch.name === this.channelName); - - return new Promise((resolve, reject) => { - channel.fetchMessages({ limit: 100 }) - .then(messages => { - new Map([...messages].reverse()).forEach(m => this.cachedLinks.push(...GetUrls(m.content))); //push all the links in each message into our links array - resolve(this); - }) - .catch(reject); - }); - } - - check(guild) { - Dns.resolve(Url.parse(this.url).host || "", err => { //check we can resolve the host, so we can throw an appropriate error if it fails - if (err) - DiscordUtil.dateError("Connection Error: Can't resolve host", err); //log our error if we can't resolve the host - else - FeedRead(this.url, (err, articles) => { //check the feed - if (err) - DiscordUtil.dateError(err); - else { - let latest = articles[0].link; //extract the latest link - latest = normaliseUrl(latest); //standardise it a bit - - //if we don't have it cached already, cache it and callback - if (!this.cachedLinks.includes(latest)) { - this.cachedLinks.push(latest); - - const channel = guild.channels.find(ch => ch.type === "text" && ch.name.toLowerCase() === this.channelName.toLowerCase()); - const role = this.roleName ? guild.roles.find(role => role.name.toLowerCase() === this.roleName.toLowerCase()) : null; - channel.send((role ? role + " " : "") + latest); - } - } - }); - }); - } - - toString() { - const blacklist = ["cachedLinks", "maxCacheSize"]; - return `\`\`\`JavaScript\n ${JSON.stringify(this, (k, v) => !blacklist.includes(k) ? v : undefined, "\t")} \`\`\``; - } -}; - -function normaliseUrl(url) { - url = url.replace("https://", "http://"); //cheaty way to get around http and https not matching - - if (Url.parse(url).host.includes("youtu")) //detect youtu.be and youtube.com - yes I know it's hacky - url = url.split("&")[0]; //quick way to chop off stuff like &feature=youtube - - url = url.replace(/(www.)?youtube.com\/watch\?v=/, "youtu.be/"); //turn full url into share url - - return url; -} \ No newline at end of file diff --git a/app/models/guild-data.js b/app/models/guild-data.js deleted file mode 100644 index d1ea352..0000000 --- a/app/models/guild-data.js +++ /dev/null @@ -1,23 +0,0 @@ -const FeedData = require("./feed-data.js"); -const Util = require("discordjs-util"); - -module.exports = class GuildData { - constructor({ id, feeds }) { - this.id = id; - this.feeds = feeds.map(feed => new FeedData(feed)); - } - - cachePastPostedLinks(guild) { - const promises = []; - - this.feeds.forEach(feed => { - promises.push(feed.updatePastPostedLinks(guild).catch(Util.dateError)); - }); - - return Promise.all(promises); - } - - checkFeeds(guilds) { - this.feeds.forEach(feed => feed.check(guilds.get(this.id))); - } -}; \ No newline at end of file diff --git a/docs/.gitrepo b/docs/.gitrepo deleted file mode 100644 index fb2ce99..0000000 --- a/docs/.gitrepo +++ /dev/null @@ -1,11 +0,0 @@ -; DO NOT EDIT (unless you know what you are doing) -; -; This subdirectory is a git "subrepo", and this file is maintained by the -; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme -; -[subrepo] - remote = git@github.com:benji7425/discord-feed-bot.wiki.git - branch = master - commit = f70e48b91074ad051eec48679d013409a79ed53e - parent = 21bc8bdaeb42f8eca6218fbe1910ddcc75d56c3c - cmdver = 0.3.1 diff --git a/docs/home.md b/docs/home.md deleted file mode 100644 index 5aeda31..0000000 --- a/docs/home.md +++ /dev/null @@ -1,3 +0,0 @@ -If browsing on GitHub, please use the sidebar on the right to navigate the wiki. - -Please use releases from the 'releases' page when installing rather than just cloning the repository yourself \ No newline at end of file diff --git a/docs/user/commands.md b/docs/user/commands.md deleted file mode 100644 index 743d4d4..0000000 --- a/docs/user/commands.md +++ /dev/null @@ -1,19 +0,0 @@ -# Commands - -There is a very basic level of interaction with the bot available via chat commands, all of which are prefixed with an exclamation mark '!' - -## User commands - -| command | action | -|--------------|-----------------------------------------------------------------------------------------| -| !help | list available commands | -| !subscribe | subscribe to feed notifications, so your username gets tagged when a new link is posted | -| !unsubscribe | unsubscribe from feed notifications | - -## Developer commands -Requires your user ID to be specified in the developers list in config.json - -| command | action | -|-------------|----------------------------| -| !logsplease | upload the log file | -| !cacheList | report all the cached URLs | \ No newline at end of file diff --git a/docs/user/configuration.md b/docs/user/configuration.md deleted file mode 100644 index 11b01e4..0000000 --- a/docs/user/configuration.md +++ /dev/null @@ -1,31 +0,0 @@ -# Configuration - -| parameter | description | -|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------| -| feedUrl | the RSS feed to check | -| channelID | report all the cached URLs | -| serverID | id of the server the bot is installed on - require to use subscriptions | -| pollingInterval | interval in ms to check the RSS feed | -| numLinksToCache | number of posted links to cache and check against before posting - turn this up if users post a lot of links in the channel | -| messageDeleteDelay | time in ms to leave response messages before deleting (eg "You have successfully subscribed") | -| youtubeMode | whether or not to convert YouTube links to their short url - recommended if you are pulling links from a YouTube channel feed | -| allowSubscriptions | whether or not to have the bot mention a role when it posts a link | -| subscribersRoleID | the ID of the role to mention when posting a link - you can find this by typing \@role in discord and copying out just the numeric part | -| developers | array of developer IDs - add a new one by putting a comma at the end of the one above and putting the ID in double quotes | - - -## How to find IDs - -- Make sure developer mode is turned on in discord - - User Settings > Appearance > Developer Mode - -| id | how to find | -|----------------------------|-----------------------------------------------| -| channelID | right click on the channel > copy ID | -| serverID | right click on the server name > copy ID | -| subscribersRoleID | type \@role in discord, copy the numeric part | -| user ID (to add developer) | right click on user > copy ID | - -## Note about subsriptions - -For subscriptions to work the bot needs the "Manage roles" permission, and needs to be in a role *higher than* the subscribers role \ No newline at end of file diff --git a/docs/user/installation.md b/docs/user/installation.md deleted file mode 100644 index 52ffa85..0000000 --- a/docs/user/installation.md +++ /dev/null @@ -1,14 +0,0 @@ -# Installation - -1. Make sure you have nodejs (v6+) and npm installed -2. Download the zip from [releases](https://github.com/benji7425/discord-feed-bot/releases) and extract -3. Open a terminal in extracted folder -4. Run `npm install` and wait for it to finish -5. Edit *config.json* to include your server/channel details (see configuration page for more info) -6. Create *bot-config.json* to include your bot token: -`{ - "token": "abc123blahblahblahyourtokengoeshere" -}` -7. Run `node feed-bot.js` - -See configuration page for more detail on configuration options \ No newline at end of file diff --git a/docs/user/troubleshooting.md b/docs/user/troubleshooting.md deleted file mode 100644 index 4bf00b2..0000000 --- a/docs/user/troubleshooting.md +++ /dev/null @@ -1,10 +0,0 @@ -# Troubleshooting - -## Bot can't add users to role - -- Make sure the bot's role has the "Manage Roles" permission -- Make sure the bot's role is higher than the subscribers role - -## Other stuff - -Feel free to contact me at the email on my GitHub profile :) \ No newline at end of file diff --git a/package.json b/package.json deleted file mode 100644 index 7ecc5cd..0000000 --- a/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "discord-bot-feed-linker", - "version": "2.1.0-b1", - "description": "", - "main": "app/index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node wrapper.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/benji7425/discord-bot-feed-linker.git" - }, - "author": "", - "license": "ISC", - "bugs": { - "url": "https://github.com/benji7425/discord-bot-feed-linker/issues" - }, - "homepage": "https://github.com/benji7425/discord-bot-feed-linker#readme", - "dependencies": { - "discord.js": "11.1.0", - "discordjs-util": "git+https://github.com/benji7425/discordjs-util.git", - "dns": "0.2.2", - "feed-read": "0.0.1", - "get-urls": "7.0.0", - "jsonfile": "3.0.1", - "shortid": "2.2.8" - } -} diff --git a/wrapper.js b/wrapper.js deleted file mode 100644 index 4efa939..0000000 --- a/wrapper.js +++ /dev/null @@ -1,20 +0,0 @@ -const Discord = require("discord.js"); -const DiscordUtil = require("discordjs-util"); - -const client = new Discord.Client(); - -process.on("uncaughtException", (err) => { - DiscordUtil.dateError("Uncaught exception!", err); -}); - -client.login(require("./token.json").token); - -client.on("ready", () => { - DiscordUtil.dateLog("Registered bot " + client.user.username); - require("./app/index.js")(client); - client.user.setGame("benji7425.github.io"); -}); - -client.on("disconnect", eventData => { - DiscordUtil.dateError("Bot was disconnected!", eventData.code, eventData.reason); -}); \ No newline at end of file diff --git a/wrapper/.eslintrc.json b/wrapper/.eslintrc.json deleted file mode 100644 index f67f1c2..0000000 --- a/wrapper/.eslintrc.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "env": { - "browser": true, - "node": true, - "commonjs": true, - "es6": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "sourceType": "module" - }, - "rules": { - "indent": [ - "warn", - "tab", - { "SwitchCase": 1} - ], - "quotes": [ - "warn", - "double" - ], - "semi": [ - "error", - "always" - ], - "no-undef": "error", - "no-unused-vars": "warn", - "eqeqeq": ["error", "always"] - } -} diff --git a/wrapper/.gitignore b/wrapper/.gitignore deleted file mode 100644 index 7f0b165..0000000 --- a/wrapper/.gitignore +++ /dev/null @@ -1,53 +0,0 @@ -# Project specific - -token.json - -### Node ### -# Logs -logs -*.log -npm-debug.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_cache -node_modules -jspm_packages - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity \ No newline at end of file diff --git a/wrapper/.gitrepo b/wrapper/.gitrepo deleted file mode 100644 index d921bc8..0000000 --- a/wrapper/.gitrepo +++ /dev/null @@ -1,11 +0,0 @@ -; DO NOT EDIT (unless you know what you are doing) -; -; This subdirectory is a git "subrepo", and this file is maintained by the -; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme -; -[subrepo] - remote = git@github.com:benji7425/shell-discord-bot.git - branch = master - commit = 0cf920f3a659f11d388a62b248073c08bf89949b - parent = 8fc554c68d7f249ca5c7307e78d81866cb130e74 - cmdver = 0.3.1 diff --git a/wrapper/.npmrc b/wrapper/.npmrc deleted file mode 100644 index 799e6bc..0000000 --- a/wrapper/.npmrc +++ /dev/null @@ -1,3 +0,0 @@ -save=true -save-exact=true -cache=node_cache diff --git a/wrapper/README.md b/wrapper/README.md deleted file mode 100644 index 9047c22..0000000 --- a/wrapper/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# Shell Discord bot - -The purpose of this is to act as a shell for other bot modules, so that a single bot user account can be used for a multi-function bot. - -## Usage - -### As a project base -- Fork/clone/merge this repo into a new one -- Run `npm init` to re-initialise as the new repo -- Run `npm install` -- Create *token.json* with your discord token: `{ "token": "1234567890" }` -- Add your modules and reference them in `var BotModules = [];` - for example: `var BotModules = [require("my-module")];` - -### As a wrapper -- Use [git subrepo](https://github.com/ingydotnet/git-subrepo) to clone this into a folder called *wrapper* in your parent project -- Update your parent project's `start` script to run `node wrapper/index.js` -- Add a reference to your parent project's main file in `var BotModules = [];` - for example: `var BotModules = [require("../app/index.js")];` - -## Creating a bot module - -Interfacing with each bot module is done by the properties in its module.exports. Available properties are: - -| property | property type | parameters | description | -|--------------|---------------|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------| -| onReady | method | bot | called on the bot ready event | -| onDisconnect | method | none | called when the bot disconnects | -| onMessage | method | bot, user, userID, channelID, message | called when the bot receives a message - identical to the 'action' property of a command, but triggered on every message (see below) | -| commands | array | N/A | commands a user can invoke - like the onMessage event, but only triggered on expected commands (see below) | - -### Commands - -A command object contains a command, and an action to invoke if the message contains that command. Each command object needs a certain set of properties: - - -| property | optional? | value | description | -|------------|-----------|--------------------------|-----------------------------------------------------------------------------------------------------------| -| command | required | string | Command to look for in the message | -| type | required | "equals" or "startsWith" | Describes whether we are looking for the message to be the exact command, or just to start with it | -| action | required | function | Callback to invoke if the command is matched (see below) | -| channelIDs | optional | array of strings | If this property is present, the command will only be triggered if sent in one of these channels | -| roleIDs | optional | array of strings | If this property is present, the command will only be triggered if send by a user with one of these roles | -| userIDs | optional | array of strings | If this property is present, the command will only be triggered if sent by one of these users | - -**Permission heirarchy** -channelIDs > roleIDs > userIDs - -Examples of commands that *won't* be triggered: -- channelIDs *contains* the channel, but userIDs *doesn't* - channelID check will pass, userID check subsequently won't -- channelIDs *doesn't contain* the channel, roleIDs or userIDs *contain* the user - channelID check will block the command, regardless of other permissions -- channelIDs *contains* the channel, roleIDs *doesn't* contain the user's roles, userIDs *contains* the user - channelID check will pass, roleID check will block - - -#### Actions - -An action is a callback function called if the specified command is found. It must take these parameters: - -| parameter | type | description | -|-----------|--------|----------------------------------------------------------------------| -| bot | object | The [discord.io](https://github.com/izy521/discord.io) client object | -| user | string | Username of the user who sent the message | -| userID | string | User ID of the user who sent the message | -| channelID | string | Channel ID of the channel the message was sent in | -| message | string | The message/command that was sent | - -Example 1: - -```JavaScript -{ - command: "ping", - type: "equals", - action: (bot, user, userID, channelID, message) => { - bot.sendMessage({ - to: channelID, - message: "pong" - }) - }, - userIDs: ["1234567890"] -} -``` - -The above example will only call *action* if the user with ID 1234567890 sends "ping" - -Example 2: - -```JavaScript -{ - command: "!define", - type: "startsWith", - action: (bot, user, userID, channelID, message) => { - bot.sendMessage({ - to: channelID, - message: getDefinition(message.split(" ")[1]) - }) - } -} -``` - -The above example expects the user to type '!define something', ie only checking for the message to start with '!define'. You are still passed the full message, so can split it up and read it however you want. -*action* will only be called if the message begins with '!define', but has no restrictions on which channel(s) or user(s) use it diff --git a/wrapper/index.js b/wrapper/index.js deleted file mode 100644 index a7cd9b8..0000000 --- a/wrapper/index.js +++ /dev/null @@ -1,93 +0,0 @@ -//node imports -const Console = require("console"); - -//external module imports -var Discord = require("discord.io"); - -var BotModules = [require("../app/index.js")()]; - -var bot; - -var EventHandlers = { - onReady: () => { - Console.info("Registered bot " + bot.username + " with id " + bot.id); - - for (let i = 0, len = BotModules.length; i < len; i++) { - let botModule = BotModules[i]; - if (botModule.onReady) botModule.onReady(bot); - } - }, - onDisconnect: (err, code) => { - Console.error("Bot was disconnected!", err, code); - - for (let i = 0, len = BotModules.length; i < len; i++) { - let botModule = BotModules[i]; - if (botModule.onDisconnect) botModule.onDisconnect(); - } - - bot.connect(); - }, - onMessage: (user, userID, channelID, message) => { - for (let i = 0, iLen = BotModules.length; i < iLen; i++) { - let botModule = BotModules[i]; - - if (botModule.commands) { - for (let j = 0, jLen = botModule.commands.length; j < jLen; j++) { - let messageTrigger = botModule.commands[j]; - - if (commandIsAllowed(messageTrigger, user, userID, channelID)) - switch (messageTrigger.type) { - case "startsWith": - if (message.startsWith(messageTrigger.command)) - messageTrigger.action(bot, user, userID, channelID, message); - break; - default: - if (message === messageTrigger.command) - messageTrigger.action(bot, user, userID, channelID, message); - } - } - } - - if (botModule.onMessage) botModule.onMessage(bot, user, userID, channelID, message); - } - } -}; - -var commandIsAllowed = (messageTrigger, user, userID, channelID) => { - //if we aren't allowed this command in this channel, disallow the command - if (messageTrigger.channelIDs && !messageTrigger.channelIDs.includes(channelID)) - return false; - - if (messageTrigger.roleIDs) { //check if we have a role constraint - var userHasPermissiveRole = false; - - messageTrigger.roleIDs.forEach((element) => { //iterate over all the allowed role IDs - if (userHasRole(userID, channelID, element)) userHasPermissiveRole = true; //check if the user has this role - }); - - if (!userHasPermissiveRole) return false; //disallow the command if the user doesn't have one of these role IDs - } - - //if this user isn't allowed, disallow the command - if (messageTrigger.userIDs && !messageTrigger.userIDs.includes(userID)) - return false; - - //if we haven't returned false by now, then the command is allowed - return true; -}; - -var userHasRole = (userID, channelID, roleID) => { - var userRoles = bot.servers[bot.channels[channelID].guild_id].members[userID].roles; - return userRoles.includes(roleID); -}; - -(() => { - bot = new Discord.Client({ - token: require("./token.json").token, - autorun: true - }); - - bot.on("ready", EventHandlers.onReady); - bot.on("disconnect", EventHandlers.onDisconnect); - bot.on("message", EventHandlers.onMessage); -})(); \ No newline at end of file diff --git a/wrapper/node_shrinkwrap/cjopus-0.0.4.tar b/wrapper/node_shrinkwrap/cjopus-0.0.4.tar deleted file mode 100644 index a462129..0000000 Binary files a/wrapper/node_shrinkwrap/cjopus-0.0.4.tar and /dev/null differ diff --git a/wrapper/node_shrinkwrap/discord.io-2.5.1.tar b/wrapper/node_shrinkwrap/discord.io-2.5.1.tar deleted file mode 100644 index aa6447b..0000000 Binary files a/wrapper/node_shrinkwrap/discord.io-2.5.1.tar and /dev/null differ diff --git a/wrapper/node_shrinkwrap/options-0.0.6.tar b/wrapper/node_shrinkwrap/options-0.0.6.tar deleted file mode 100644 index cf9534b..0000000 Binary files a/wrapper/node_shrinkwrap/options-0.0.6.tar and /dev/null differ diff --git a/wrapper/node_shrinkwrap/tweetnacl-0.14.5.tar b/wrapper/node_shrinkwrap/tweetnacl-0.14.5.tar deleted file mode 100644 index 64ce1c8..0000000 Binary files a/wrapper/node_shrinkwrap/tweetnacl-0.14.5.tar and /dev/null differ diff --git a/wrapper/node_shrinkwrap/ultron-1.0.2.tar b/wrapper/node_shrinkwrap/ultron-1.0.2.tar deleted file mode 100644 index d6b1f06..0000000 Binary files a/wrapper/node_shrinkwrap/ultron-1.0.2.tar and /dev/null differ diff --git a/wrapper/node_shrinkwrap/ws-1.1.4.tar b/wrapper/node_shrinkwrap/ws-1.1.4.tar deleted file mode 100644 index 184dcb2..0000000 Binary files a/wrapper/node_shrinkwrap/ws-1.1.4.tar and /dev/null differ diff --git a/wrapper/npm-shrinkwrap.json b/wrapper/npm-shrinkwrap.json deleted file mode 100644 index 1341fe3..0000000 --- a/wrapper/npm-shrinkwrap.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "discord-bot", - "version": "1.0.0", - "dependencies": { - "cjopus": { - "version": "0.0.4", - "from": "cjopus@>=0.0.4 <0.0.5", - "resolved": "./node_shrinkwrap/cjopus-0.0.4.tar" - }, - "discord.io": { - "version": "2.5.1", - "from": "discord.io@latest", - "resolved": "./node_shrinkwrap/discord.io-2.5.1.tar" - }, - "options": { - "version": "0.0.6", - "from": "options@>=0.0.5", - "resolved": "./node_shrinkwrap/options-0.0.6.tar" - }, - "tweetnacl": { - "version": "0.14.5", - "from": "tweetnacl@>=0.14.0 <0.15.0", - "resolved": "./node_shrinkwrap/tweetnacl-0.14.5.tar" - }, - "ultron": { - "version": "1.0.2", - "from": "ultron@>=1.0.0 <1.1.0", - "resolved": "./node_shrinkwrap/ultron-1.0.2.tar" - }, - "ws": { - "version": "1.1.4", - "from": "ws@>=1.1.0 <2.0.0", - "resolved": "./node_shrinkwrap/ws-1.1.4.tar" - } - } -} \ No newline at end of file diff --git a/wrapper/package.json b/wrapper/package.json deleted file mode 100644 index 51ad0d6..0000000 --- a/wrapper/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "discord-bot", - "version": "1.0.0", - "description": "The purpose of this is to act as a shell for other bot modules, so that a single bot user account can be used for a multi-function bot.", - "main": "index.js", - "dependencies": { - "discord.io": "2.5.1" - }, - "devDependencies": {}, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/benji7425/discord-bot-wrapper.git" - }, - "author": "", - "license": "ISC", - "bugs": { - "url": "https://github.com/benji7425/discord-bot-wrapper/issues" - }, - "homepage": "https://github.com/benji7425/discord-bot-wrapper#readme" -}