diff --git a/library.js b/library.js index 5290e8b..a5b9d0c 100644 --- a/library.js +++ b/library.js @@ -1,79 +1,91 @@ -(function(module) { - 'use strict'; - - var User = require.main.require('./src/user'); - var Topics = require.main.require('./src/topics'); - var Categories = require.main.require('./src/categories'); - var translator = require.main.require('./src/translator'); - var meta = require.main.require('./src/meta'); - var nconf = require.main.require('nconf'); - var async = require.main.require('async'); - - var Discord = require('discord.js'); - - var hook = null; - var forumURL = nconf.get('url'); - - var plugin = { - config: { - webhookURL: '', - maxLength: '', - postCategories: '', - topicsOnly: '', - messageContent: '' - }, - regex: /https:\/\/discord(?:app)?\.com\/api\/webhooks\/([0-9]+?)\/(.+?)$/ - }; - - plugin.init = function(params, callback) { - function render(req, res, next) { - res.render('admin/plugins/discord-notification', {}); +'use strict'; + +const user = require.main.require('./src/user'); +const topics = require.main.require('./src/topics'); +const categories = require.main.require('./src/categories'); +const translator = require.main.require('./src/translator'); +const meta = require.main.require('./src/meta'); +const nconf = require.main.require('nconf'); +const async = require.main.require('async'); + +const Discord = require('discord.js'); + +let hook = null; +var forumURL = nconf.get('url'); + +const renderAdmin = async function(req, res, next) { + const allCategories = await categories.buildForSelectAll(['value', 'text']); + res.render( + 'admin/plugins/discord-notification', + { + title: '[[discord-notification:title]]', + allCategories: allCategories, } + ); +} - params.router.get('/admin/plugins/discord-notification', params.middleware.admin.buildHeader, render); - params.router.get('/api/admin/plugins/discord-notification', render); +const discordNotification = { + config: {}, - meta.settings.get('discord-notification', function(err, settings) { - for (var prop in plugin.config) { - if (settings.hasOwnProperty(prop)) { - plugin.config[prop] = settings[prop]; - } - } + onLoad: async function(params) { + const routeHelpers = require.main.require('./src/routes/helpers'); + routeHelpers.setupAdminPageRoute(params.router, '/admin/plugins/discord-notification', renderAdmin); + discordNotification.init(); + }, - // Parse Webhook URL (1: ID, 2: Token) - var match = plugin.config['webhookURL'].match(plugin.regex); + init: function () { + // Load saved config + const _self = this; + const defaults = { + webhookURL: '', + maxLength: 1024, + postCategories: '', + topicsOnly: 'off', + messageContent: '', + }; + const notCheckboxes = ['webhookURL', 'maxLength', 'postCategories', 'messageContent']; - if (match) { - hook = new Discord.WebhookClient(match[1], match[2]); + meta.settings.get('discord-notification', (err, options) => { + if (err) { + winston.warn(`[plugin//discord-notification] Unable to retrieve settings, assuming defaults: ${err.message}`); } - }); - callback(); + Object.keys(defaults).forEach((field) => { + // If not set in config (nil) + if (!options.hasOwnProperty(field)) { + _self.config[field] = defaults[field]; + } else if (!notCheckboxes.includes(field)) { + _self.config[field] = options[field] === 'on'; + } else { + _self.config[field] = options[field]; + } + }); + }); }, - plugin.postSave = function(post) { + postSave: function(post) { post = post.post; - var topicsOnly = plugin.config['topicsOnly'] || 'off'; + var topicsOnly = discordNotification.config['topicsOnly'] || 'off'; if (topicsOnly === 'off' || (topicsOnly === 'on' && post.isMain)) { var content = post.content; async.parallel({ user: function(callback) { - User.getUserFields(post.uid, ['username', 'picture'], callback); + user.getUserFields(post.uid, ['username', 'picture'], callback); }, topic: function(callback) { - Topics.getTopicFields(post.tid, ['title', 'slug'], callback); + topics.getTopicFields(post.tid, ['title', 'slug'], callback); }, category: function(callback) { - Categories.getCategoryFields(post.cid, ['name', 'bgColor'], callback); + categories.getCategoryFields(post.cid, ['name', 'bgColor'], callback); } }, function(err, data) { - var categories = JSON.parse(plugin.config['postCategories']); + var categories = JSON.parse(discordNotification.config['postCategories']); if (!categories || categories.indexOf(String(post.cid)) >= 0) { // Trim long posts: - var maxQuoteLength = plugin.config['maxLength'] || 1024; + var maxQuoteLength = discordNotification.config['maxLength'] || 1024; if (content.length > maxQuoteLength) { content = content.substring(0, maxQuoteLength) + '...'; } // Ensure absolute thumbnail URL if an avatar exists: @@ -86,27 +98,27 @@ } // Add custom message: - var messageContent = plugin.config['messageContent'] || ''; - + const messageContent = discordNotification.config['messageContent'] || ''; + // Make the rich embed: - var embed = new Discord.MessageEmbed() + const embed = new Discord.EmbedBuilder() .setColor(data.category.bgColor) .setURL(forumURL + '/topic/' + data.topic.slug) .setTitle(data.category.name + ': ' + data.topic.title) .setDescription(content) - .setFooter(data.user.username, thumbnail) + .setFooter({ text: data.user.username, iconURL: thumbnail }) .setTimestamp(); - // Send notification: if (hook) { - hook.send(messageContent, {embeds: [embed]}).catch(console.error); + hook.send({content: messageContent, embeds: [embed]}) + .catch(console.error); } } }); } }, - plugin.addAdminMenu = function(header, callback) { + addAdminMenu: function(header, callback) { translator.translate('[[discord-notification:title]]', function(title) { header.plugins.push({ route : '/plugins/discord-notification', @@ -116,8 +128,33 @@ callback(null, header); }); - }; + }, + + getConfig: async (config) => { + let { webhookURL, maxLength, postCategories, topicsOnly, messageContent } = await meta.settings.get('discord-notification'); + + try { + // Parse Webhook URL (1: ID, 2: Token) + const discordRegex = /https:\/\/discord(?:app)?\.com\/api\/webhooks\/([0-9]+?)\/(.+?)$/; + const match = webhookURL.match(discordRegex); + + if (match) { + hook = new Discord.WebhookClient({ id: match[1], token: match[2] }); + } + } catch (e) { + // Do nothing + } - module.exports = plugin; + config.discordNotification = { + webhookURL, + maxLength, + postCategories, + topicsOnly, + messageContent + }; + + return config; + }, +}; -}(module)); +module.exports = discordNotification; \ No newline at end of file diff --git a/package.json b/package.json index 17620b8..954f02c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nodebb-plugin-discord-notification", "title": "Discord Notifications for NodeBB", - "version": "1.5.0", + "version": "1.6.0", "description": "Send notifications of new posts and topics via Discord webhook.", "keywords": [ "discord", @@ -24,9 +24,9 @@ "license": "MIT", "main": "library.js", "dependencies": { - "discord.js": "^12.4.1" + "discord.js": "^14.25.1" }, "nbbpm": { - "compatibility": "^2.0.0" + "compatibility": "^4.0.0" } } diff --git a/plugin.json b/plugin.json index 25fea1e..baa62d6 100644 --- a/plugin.json +++ b/plugin.json @@ -5,8 +5,9 @@ "url": "https://github.com/amargon/nodebb-plugin-discord-notification", "library": "./library.js", "hooks": [ - { "hook": "static:app.load", "method": "init" }, + { "hook": "static:app.load", "method": "onLoad" }, { "hook": "filter:admin.header.build", "method": "addAdminMenu" }, + { "hook": "filter:config.get", "method": "getConfig" }, { "hook": "action:post.save", "method": "postSave"} ], "modules": { @@ -14,5 +15,6 @@ }, "templates": "./public/templates", "languages": "./languages", + "defaultLang": "en_GB", "settingsRoute": "/admin/plugins/discord-notification" } diff --git a/public/src/admin.js b/public/src/admin.js index f8d9d9a..5dac784 100644 --- a/public/src/admin.js +++ b/public/src/admin.js @@ -1,28 +1,39 @@ -const settings = require('settings'); -const hooks = require('hooks'); +'use strict'; -hooks.onPage('action:ajaxify.end', () => { - // note: this is probably deprecated in core soon... - socket.emit('categories.get', function(err, data) { - categories = data; - for (var i = 0; i < categories.length; ++i) { - $('#postCategories').append(''); - } - }); -}) +define('admin/plugins/discord-notification', ['settings'], function (Settings) { + let discordNotification = {}; -settings.load('discord-notification', $('.discord-notification-settings')); + discordNotification.init = function () { + Settings.load('discord-notification', $('.discord-notification-settings'), function (err, settings) { + if (err) { + settings = {}; + } + + var defaults = { + webhookURL: '', + maxLength: 1024, + postCategories: '', + topicsOnly: false, + messageContent: '', + }; -$('#save').on('click', function() { - settings.save('discord-notification', $('.discord-notification-settings'), function() { - app.alert({ - type: 'success', - alert_id: 'discord-notification-saved', - title: 'Settings Saved', - message: 'Please reload your NodeBB to apply these settings', - clickfn: function() { - socket.emit('admin.reload'); + // Set defaults + for (const setting of Object.keys(defaults)) { + if (!settings.hasOwnProperty(setting)) { + if (typeof defaults[setting] === 'boolean') { + $('#' + setting).prop('checked', defaults[setting]); + } else { + $('#' + setting).val(defaults[setting]); + } + } } }); - }); + + $('#save').on('click', function () { + console.log('Saving settings'); + Settings.save('discord-notification', $('.discord-notification-settings')); + }); + }; + + return discordNotification; }); diff --git a/public/templates/admin/plugins/discord-notification.tpl b/public/templates/admin/plugins/discord-notification.tpl index 6dab9c0..6589d79 100644 --- a/public/templates/admin/plugins/discord-notification.tpl +++ b/public/templates/admin/plugins/discord-notification.tpl @@ -1,42 +1,45 @@ -
-
-
[[discord-notification:webhook]]
-
-
- - -

[[discord-notification:webhook-help]]

+
+ + +
+
[[discord-notification:webhook]]
+
+
+ + +

[[discord-notification:webhook-help]]

+
-
-
-
[[discord-notification:notification]]
-
-
- - -

[[discord-notification:notification-max-length-help]]

-
-
- - -
-
- -
-
- - -

[[discord-notification:message-help]]

+
+
[[discord-notification:notification]]
+
+
+ + +

[[discord-notification:notification-max-length-help]]

+
+
+
+ + +
+
+
+ + +
+
+ + +

[[discord-notification:message-help]]

+
-
- - - + +
\ No newline at end of file diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index c5a1920..0000000 --- a/yarn.lock +++ /dev/null @@ -1,117 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@discordjs/collection@^0.1.6": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.1.6.tgz#9e9a7637f4e4e0688fd8b2b5c63133c91607682c" - integrity sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ== - -"@discordjs/form-data@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@discordjs/form-data/-/form-data-3.0.1.tgz#5c9e6be992e2e57d0dfa0e39979a850225fb4697" - integrity sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -discord.js@^12.4.1: - version "12.5.3" - resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-12.5.3.tgz#56820d473c24320871df9ea0bbc6b462f21cf85c" - integrity sha512-D3nkOa/pCkNyn6jLZnAiJApw2N9XrIsXUAdThf01i7yrEuqUmDGc7/CexVWwEcgbQR97XQ+mcnqJpmJ/92B4Aw== - dependencies: - "@discordjs/collection" "^0.1.6" - "@discordjs/form-data" "^3.0.1" - abort-controller "^3.0.0" - node-fetch "^2.6.1" - prism-media "^1.2.9" - setimmediate "^1.0.5" - tweetnacl "^1.0.3" - ws "^7.4.4" - -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - -mime-db@1.51.0: - version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== - -mime-types@^2.1.12: - version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== - dependencies: - mime-db "1.51.0" - -node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -prism-media@^1.2.9: - version "1.3.2" - resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.3.2.tgz#a1f04423ec15d22f3d62b1987b6a25dc49aad13b" - integrity sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g== - -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -tweetnacl@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -ws@^7.4.4: - version "7.5.7" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" - integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==