From 20a188733d735c474607923405b2ef298d21a279 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 7 May 2020 15:50:46 +0200 Subject: [PATCH] Add auto mute plugin --- peertube-plugin-auto-mute/README.md | 44 ++++++ peertube-plugin-auto-mute/main.js | 148 ++++++++++++++++++ peertube-plugin-auto-mute/package-lock.json | 49 ++++++ peertube-plugin-auto-mute/package.json | 23 +++ .../tests/blocklist-sample.json | 10 ++ peertube-plugin-hello-world/package.json | 4 + 6 files changed, 278 insertions(+) create mode 100644 peertube-plugin-auto-mute/README.md create mode 100644 peertube-plugin-auto-mute/main.js create mode 100644 peertube-plugin-auto-mute/package-lock.json create mode 100644 peertube-plugin-auto-mute/package.json create mode 100644 peertube-plugin-auto-mute/tests/blocklist-sample.json diff --git a/peertube-plugin-auto-mute/README.md b/peertube-plugin-auto-mute/README.md new file mode 100644 index 0000000..dbcc838 --- /dev/null +++ b/peertube-plugin-auto-mute/README.md @@ -0,0 +1,44 @@ +# Auto mute plugin for PeerTube + +Auto mute accounts or instances based on public blocklists. + +## Format + +This plugins expect the following JSON format from public blocklists: + +``` +{ + data: { + value: string + action?: 'add' | 'remove' // Default is 'add' + }[] +} +``` + +For example: + +``` +[ + { + value: 'peertube.cpy.re' + }, + { + value: 'root@peertube.cpy.re' + } +] +``` + +This plugin does not apply a diff, so if you want to remove an entity from the blocklist, add `action: 'remove'` to the object. + +For example, to revert `peertube.cpy.re` from the blocklist, update the JSON: + +``` +[ + { + value: 'peertube.cpy.re', + action: 'remove' + }, + { + value: 'root@peertube.cpy.re' + } +] diff --git a/peertube-plugin-auto-mute/main.js b/peertube-plugin-auto-mute/main.js new file mode 100644 index 0000000..ca3648d --- /dev/null +++ b/peertube-plugin-auto-mute/main.js @@ -0,0 +1,148 @@ +const simpleGet = require('simple-get') + +const store = { + urls: [], + checkIntervalSeconds: null, + alreadyAdded: new Set(), + alreadyRemoved: new Set(), + serverAccountId: null +} + +async function register ({ + settingsManager, + peertubeHelpers, + registerSetting +}) { + const { logger } = peertubeHelpers + + registerSetting({ + name: 'blocklist-urls', + label: 'Blocklist URLs (one per line)', + type: 'input-textarea', + private: true + }) + + registerSetting({ + name: 'check-seconds-interval', + label: 'Blocklist check frequency (seconds)', + type: 'input-textarea', + private: true, + default: 3600 // 1 Hour + }) + + const serverActor = await peertubeHelpers.server.getServerActor() + store.serverAccountId = serverActor.Account.id + + const settings = await settingsManager.getSettings([ 'check-seconds-interval', 'blocklist-urls' ]) + + await load(peertubeHelpers, settings['blocklist-urls'], settings['check-seconds-interval']) + + settingsManager.onSettingsChange(settings => { + load(peertubeHelpers, settings['blocklist-urls'], settings['check-seconds-interval']) + .catch(err => logger.error('Cannot load auto mute plugin.', { err })) + }) + + runCheckForever(peertubeHelpers) +} + +async function unregister () { + return +} + +module.exports = { + register, + unregister +} + +// ############################################################################ + +async function load (peertubeHelpers, blocklistUrls, checkIntervalSeconds) { + const { logger } = peertubeHelpers + + store.checkIntervalSeconds = checkIntervalSeconds + + store.urls = (blocklistUrls || '').split('\n') + .filter(url => !!url) + + if (store.urls.length === 0) { + logger.info('Do not load auto mute plugin because of empty blocklist URLs.') + return + } + + logger.info('Loaded %d blocklist URLs for auto mute plugin.', store.urls.length, { urls: store.urls }) +} + +async function runCheckForever (peertubeHelpers) { + const { logger } = peertubeHelpers + + if (store.urls.length === 0) return runLater() + + for (const url of store.urls) { + try { + const { data } = await get(url) + + if (Array.isArray(data.data) === false) { + throw new Error('JSON response is not valid.') + } + + for (const entity of data.data) { + if (!entity.value) throw new Error('JSON entity is not valid.') + + if (entity.action === 'remove') await removeEntity(peertubeHelpers, entity.value) + else await addEntity(peertubeHelpers, entity.value) + } + } catch (err) { + logger.warn('Cannot get mute blocklist from %s.', url, { err }) + } + } + + runLater() +} + +function runLater () { + setTimeout(runCheckForever, store.checkIntervalSeconds * 1000) +} + +function get (url) { + return new Promise((resolve, reject) => { + simpleGet({ url, json: true }, function (err, res, data) { + if (err) return reject(err) + + return resolve({ res, data }) + }) + }) +} + +function addEntity (peertubeHelpers, value) { + const moderation = { peertubeHelpers } + + if (store.alreadyAdded.has(value)) return + + store.alreadyRemoved.delete(value) + store.alreadyAdded.add(value) + + // Account + if (value.includes('@')) { + return moderation.blockAccount({ byAccountId: store.serverAccountId, handleToBlock: value }) + } + + // Server + return moderation.blockServer({ byAccountId: store.serverAccountId, hostToBlock: value }) +} + +function removeEntity (peertubeHelpers, value) { + const moderation = { peertubeHelpers } + + if (store.alreadyRemoved.has(value)) return + + store.alreadyAdded.delete(value) + store.alreadyRemoved.add(value) + + // Account + if (value.includes('@')) { + return moderation.blockAccount({ byAccountId: store.serverAccountId, handleToUnblock: value }) + } + + // Server + return moderation.blockAccount({ byAccountId: store.serverAccountId, hostToUnblock: value }) +} diff --git a/peertube-plugin-auto-mute/package-lock.json b/peertube-plugin-auto-mute/package-lock.json new file mode 100644 index 0000000..b638c3e --- /dev/null +++ b/peertube-plugin-auto-mute/package-lock.json @@ -0,0 +1,49 @@ +{ + "name": "peertube-plugin-auto-mute", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "requires": { + "mimic-response": "^2.0.0" + } + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/peertube-plugin-auto-mute/package.json b/peertube-plugin-auto-mute/package.json new file mode 100644 index 0000000..2c093ed --- /dev/null +++ b/peertube-plugin-auto-mute/package.json @@ -0,0 +1,23 @@ +{ + "name": "peertube-plugin-auto-mute", + "version": "0.0.1", + "description": "Auto mute plugin for PeerTube", + "engine": { + "peertube": ">=2.2.0" + }, + "keywords": [ + "peertube", + "plugin" + ], + "homepage": "https://framagit.org/framasoft/peertube/official-plugins/tree/master/peertube-plugin-auto-mute", + "author": "Chocobozzz", + "bugs": "https://framagit.org/framasoft/peertube/official-plugins/issues", + "library": "./main.js", + "staticDirs": {}, + "css": [], + "clientScripts": [], + "translations": {}, + "dependencies": { + "simple-get": "^3.1.0" + } +} diff --git a/peertube-plugin-auto-mute/tests/blocklist-sample.json b/peertube-plugin-auto-mute/tests/blocklist-sample.json new file mode 100644 index 0000000..6c48881 --- /dev/null +++ b/peertube-plugin-auto-mute/tests/blocklist-sample.json @@ -0,0 +1,10 @@ +{ + "data": [ + { + "value": "peertube.cpy.re" + }, + { + "value": "peertube3.cpy.re" + } + ] +} diff --git a/peertube-plugin-hello-world/package.json b/peertube-plugin-hello-world/package.json index 58516a5..54fa8be 100644 --- a/peertube-plugin-hello-world/package.json +++ b/peertube-plugin-hello-world/package.json @@ -32,6 +32,10 @@ { "script": "client/search-client-plugin.js", "scopes": [ "search" ] + }, + { + "script": "client/login-client-plugin.js", + "scopes": [ "login" ] } ], "translations": {