peertube-theme-nctv-dark/peertube-plugin-auto-mute/main.js
2020-05-11 10:13:38 +02:00

229 lines
6.6 KiB
JavaScript

const simpleGet = require('simple-get')
const store = {
urls: [],
checkIntervalSeconds: null,
alreadyAdded: new Set(),
alreadyRemoved: new Set(),
serverAccountId: null,
timeout: null
}
async function register ({
settingsManager,
storageManager,
peertubeHelpers,
registerSetting,
getRouter
}) {
const { logger, database, server } = 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
})
registerSetting({
name: 'expose-mute-list',
label: 'Publicly expose my mute list',
type: 'input-checkbox',
private: true,
default: false
})
const serverActor = await peertubeHelpers.server.getServerActor()
store.serverAccountId = serverActor.Account.id
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 mute plugin.', { err }))
})
const router = getRouter()
router.get('/api/v1/mute-list', async (req, res) => {
try {
const setting = await settingsManager.getSetting('expose-mute-list')
if (setting !== true) return res.sendStatus(403)
const serverActor = await server.getServerActor()
const serverAccountId = serverActor.Account.id
const [ serverMutes, accountMutes ] = await Promise.all([
database.query(
'SELECT "server"."host", "serverBlocklist"."updatedAt" FROM "serverBlocklist" ' +
'INNER JOIN server ON server.id = "serverBlocklist"."targetServerId" WHERE "serverBlocklist"."accountId" = ' + serverAccountId,
{ type: 'SELECT' }
),
database.query(
'SELECT "actor"."preferredUsername", "server"."host", "accountBlocklist"."updatedAt" FROM "accountBlocklist" ' +
'INNER JOIN account ON account.id = "accountBlocklist"."targetAccountId" ' +
'INNER JOIN actor ON actor.id = account."actorId" ' +
'INNER JOIN server ON server.id = actor."serverId" WHERE "accountBlocklist"."accountId" = ' + serverAccountId,
{ type: 'SELECT' }
)
])
let result = serverMutes.map(m => ({ value: m.host, updatedAt: m.updatedAt }))
result = result.concat(accountMutes.map(m => ({ value: `${m.preferredUsername}@${m.host}`, updatedAt: m.updatedAt })))
return res.json({
data: result
})
} catch (err) {
logger.error('Error in mute list endpoint.', { err })
res.sendStatus(500)
}
})
}
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 mute plugin because of empty blocklist URLs.')
return
}
logger.info('Loaded %d blocklist URLs for auto mute 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) {
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.')
// 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 get mute blocklist 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 mute 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 })
})
})
}
function addEntity (peertubeHelpers, value) {
const { moderation, logger } = peertubeHelpers
if (store.alreadyAdded.has(value)) return
store.alreadyRemoved.delete(value)
store.alreadyAdded.add(value)
logger.info('Auto mute %s from blocklist.', 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, logger } = peertubeHelpers
if (store.alreadyRemoved.has(value)) return
store.alreadyAdded.delete(value)
store.alreadyRemoved.add(value)
logger.info('Auto removing mute %s from blocklist.', value)
// Account
if (value.includes('@')) {
return moderation.unblockAccount({ byAccountId: store.serverAccountId, handleToUnblock: value })
}
// Server
return moderation.unblockServer({ byAccountId: store.serverAccountId, hostToUnblock: value })
}