229 lines
6.6 KiB
JavaScript
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 })
|
|
}
|