Add auto block videos plugin
This commit is contained in:
		
							
								
								
									
										60
									
								
								peertube-plugin-auto-block-videos/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								peertube-plugin-auto-block-videos/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | # Auto block videos plugin for PeerTube | ||||||
|  |  | ||||||
|  | Auto block videos based on public blocklists. | ||||||
|  |  | ||||||
|  | ## Block lists | ||||||
|  |  | ||||||
|  | **Add your public list here** | ||||||
|  |  | ||||||
|  | ## Blocklist URL format | ||||||
|  |  | ||||||
|  | This plugin expects the following JSON format from public blocklists: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | { | ||||||
|  |   data: { | ||||||
|  |     value: string | ||||||
|  |     action?: 'add' | 'remove' // Default is 'add' | ||||||
|  |     updatedAt?: string // ISO 8601 | ||||||
|  |   }[] | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | For example: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | { | ||||||
|  |   data: [ | ||||||
|  |     { | ||||||
|  |       value: 'https://framatube.org/videos/watch/37938234-ddf2-46d7-8967-8ac84820d5cd' | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       value: 'https://peertube.cpy.re/videos/watch/f78a97f8-a142-4ce1-a5bd-154bf9386504', | ||||||
|  |       updatedAt: '2020-05-07T14:42:48.954Z' | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | 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 `https://peertube.cpy.re/videos/watch/f78a97f8-a142-4ce1-a5bd-154bf9386504` from the blocklist, update the JSON: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | { | ||||||
|  |   data: [ | ||||||
|  |     { | ||||||
|  |       value: 'https://peertube.cpy.re/videos/watch/f78a97f8-a142-4ce1-a5bd-154bf9386504', | ||||||
|  |       action: 'remove' | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       value: 'https://framatube.org/videos/watch/37938234-ddf2-46d7-8967-8ac84820d5cd' | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The purpose of the `updatedAt` field is to not override admin blocks/unblocks: | ||||||
|  |  * Plugin auto block video A with an `updatedAt: '2020-05-07T14:42:48.954Z'` | ||||||
|  |  * Admin thinks this video is fine so it unblocks video A | ||||||
|  |  * On another check, the plugin won't re-block the account A because the `updatedAt` is before the last check | ||||||
							
								
								
									
										181
									
								
								peertube-plugin-auto-block-videos/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								peertube-plugin-auto-block-videos/main.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,181 @@ | |||||||
|  | const simpleGet = require('simple-get') | ||||||
|  |  | ||||||
|  | const store = { | ||||||
|  |   urls: [], | ||||||
|  |   checkIntervalSeconds: null, | ||||||
|  |   alreadyAdded: new Set(), | ||||||
|  |   alreadyRemoved: new Set(), | ||||||
|  |   timeout: null | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function register ({ | ||||||
|  |   settingsManager, | ||||||
|  |   storageManager, | ||||||
|  |   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', | ||||||
|  |     private: true, | ||||||
|  |     default: 3600 // 1 Hour | ||||||
|  |   }) | ||||||
|  |  | ||||||
|  |   const settings = await settingsManager.getSettings([ 'check-seconds-interval', 'blocklist-urls' ]) | ||||||
|  |  | ||||||
|  |   await load(peertubeHelpers, storageManager, settings['blocklist-urls'], settings['check-seconds-interval']) | ||||||
|  |  | ||||||
|  |   settingsManager.onSettingsChange(settings => { | ||||||
|  |     load(peertubeHelpers, storageManager, settings['blocklist-urls'], settings['check-seconds-interval']) | ||||||
|  |       .catch(err => logger.error('Cannot load auto block videos plugin.', { err })) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function unregister () { | ||||||
|  |   return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = { | ||||||
|  |   register, | ||||||
|  |   unregister | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ############################################################################ | ||||||
|  |  | ||||||
|  | async function load (peertubeHelpers, storageManager, blocklistUrls, checkIntervalSeconds) { | ||||||
|  |   const { logger } = peertubeHelpers | ||||||
|  |  | ||||||
|  |   if (store.timeout) clearTimeout(store.timeout) | ||||||
|  |  | ||||||
|  |   store.checkIntervalSeconds = checkIntervalSeconds | ||||||
|  |  | ||||||
|  |   store.urls = (blocklistUrls || '').split('\n') | ||||||
|  |                                     .filter(url => !!url) | ||||||
|  |  | ||||||
|  |   if (store.urls.length === 0) { | ||||||
|  |     logger.info('Do not load auto block videos plugin because of empty blocklist URLs.') | ||||||
|  |     return | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   logger.info('Loaded %d blocklist URLs for auto block videos plugin.', store.urls.length, { urls: store.urls }) | ||||||
|  |  | ||||||
|  |   runLater(peertubeHelpers, storageManager) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function runCheck (peertubeHelpers, storageManager) { | ||||||
|  |   const { logger } = peertubeHelpers | ||||||
|  |  | ||||||
|  |   if (store.urls.length === 0) return runLater(peertubeHelpers, storageManager) | ||||||
|  |  | ||||||
|  |   let lastChecks = await storageManager.getData('last-checks') | ||||||
|  |   if (!lastChecks) lastChecks = {} | ||||||
|  |  | ||||||
|  |   const newLastCheck = {} | ||||||
|  |  | ||||||
|  |   for (const url of store.urls) { | ||||||
|  |     try { | ||||||
|  |       const { data } = await get(url) | ||||||
|  |       newLastCheck[url] = new Date().toISOString() | ||||||
|  |  | ||||||
|  |       const lastCheckTime = lastChecks[url] | ||||||
|  |         ? new Date(lastChecks[url]).getTime() | ||||||
|  |         : 0 | ||||||
|  |  | ||||||
|  |       if (Array.isArray(data.data) === false) { | ||||||
|  |         logger.error('JSON response is not valid from %s.', { data }) | ||||||
|  |         continue | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       for (const entity of data.data) { | ||||||
|  |         if (!entity.value) { | ||||||
|  |           logger.error('JSON entity is not valid.', { entity }) | ||||||
|  |           continue | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // We already checked this entity? | ||||||
|  |         if (entity.updatedAt) { | ||||||
|  |           const updatedAtTime = new Date(entity.updatedAt).getTime() | ||||||
|  |  | ||||||
|  |           if (updatedAtTime < lastCheckTime) continue | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (entity.action === 'remove') await removeEntity(peertubeHelpers, entity.value) | ||||||
|  |         else await addEntity(peertubeHelpers, entity.value) | ||||||
|  |       } | ||||||
|  |     } catch (err) { | ||||||
|  |       logger.warn('Cannot auto block videos from %s.', url, { err }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   await storageManager.storeData('last-checks', newLastCheck) | ||||||
|  |  | ||||||
|  |   runLater(peertubeHelpers, storageManager) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function runLater (peertubeHelpers, storageManager) { | ||||||
|  |   const { logger } = peertubeHelpers | ||||||
|  |  | ||||||
|  |   logger.debug('Will run auto videos block check in %d seconds.', store.checkIntervalSeconds) | ||||||
|  |  | ||||||
|  |   store.timeout = setTimeout(() => { | ||||||
|  |     runCheck(peertubeHelpers, storageManager) | ||||||
|  |   }, store.checkIntervalSeconds * 1000) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function get (url) { | ||||||
|  |   return new Promise((resolve, reject) => { | ||||||
|  |     simpleGet.concat({ url, method: 'GET', json: true }, function (err, res, data) { | ||||||
|  |       if (err) return reject(err) | ||||||
|  |  | ||||||
|  |       return resolve({ res, data }) | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function addEntity (peertubeHelpers, value) { | ||||||
|  |   const { moderation, videos, logger } = peertubeHelpers | ||||||
|  |  | ||||||
|  |   if (store.alreadyAdded.has(value)) return | ||||||
|  |  | ||||||
|  |   store.alreadyRemoved.delete(value) | ||||||
|  |   store.alreadyAdded.add(value) | ||||||
|  |  | ||||||
|  |   const video = await videos.loadByUrl(value) | ||||||
|  |   if (!video) return | ||||||
|  |  | ||||||
|  |   if (video.remote !== true) { | ||||||
|  |     logger.info('Do not auto block our own video %s.', value) | ||||||
|  |     return | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   logger.info('Auto block video %s from blocklist.', value) | ||||||
|  |  | ||||||
|  |   const reason = 'Automatically blocked from auto block plugin.' | ||||||
|  |   return moderation.blacklistVideo({ videoIdOrUUID: video.id, createOptions: { reason } }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function removeEntity (peertubeHelpers, value) { | ||||||
|  |   const { moderation, logger, videos } = peertubeHelpers | ||||||
|  |  | ||||||
|  |   if (store.alreadyRemoved.has(value)) return | ||||||
|  |  | ||||||
|  |   store.alreadyAdded.delete(value) | ||||||
|  |   store.alreadyRemoved.add(value) | ||||||
|  |  | ||||||
|  |   const video = await videos.loadByUrl(value) | ||||||
|  |   if (!video) return | ||||||
|  |  | ||||||
|  |   logger.info('Auto removing video %s from blocklist.', value) | ||||||
|  |  | ||||||
|  |   return moderation.unblacklistVideo({ videoIdOrUUID: video.id }) | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								peertube-plugin-auto-block-videos/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								peertube-plugin-auto-block-videos/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | { | ||||||
|  |   "name": "peertube-plugin-auto-block-videos", | ||||||
|  |   "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=" | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								peertube-plugin-auto-block-videos/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								peertube-plugin-auto-block-videos/package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | { | ||||||
|  |   "name": "peertube-plugin-auto-block-videos", | ||||||
|  |   "version": "0.0.1", | ||||||
|  |   "description": "Auto block videos plugin for PeerTube", | ||||||
|  |   "engine": { | ||||||
|  |     "peertube": ">=2.2.0" | ||||||
|  |   }, | ||||||
|  |   "keywords": [ | ||||||
|  |     "peertube", | ||||||
|  |     "plugin" | ||||||
|  |   ], | ||||||
|  |   "homepage": "https://framagit.org/framasoft/peertube/official-plugins/tree/master/peertube-plugin-auto-block-videos", | ||||||
|  |   "author": "Chocobozzz", | ||||||
|  |   "bugs": "https://framagit.org/framasoft/peertube/official-plugins/issues", | ||||||
|  |   "library": "./main.js", | ||||||
|  |   "staticDirs": {}, | ||||||
|  |   "css": [], | ||||||
|  |   "clientScripts": [], | ||||||
|  |   "translations": {}, | ||||||
|  |   "dependencies": { | ||||||
|  |     "simple-get": "^3.1.0" | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | { | ||||||
|  |   "data": [ | ||||||
|  |     { | ||||||
|  |       "value": "peertube.cpy.re" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "value": "peertube3.cpy.re" | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user