From 5de7c59066e8817d217130c6a5ff35c51dfa1f1f Mon Sep 17 00:00:00 2001 From: substack Date: Fri, 3 Jul 2020 17:36:59 -1000 Subject: [PATCH 1/2] convert tabs to spaces --- src/cabal-details.js | 4 ++-- src/commands.js | 34 +++++++++++++++++----------------- src/moderation.js | 10 +++++----- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/cabal-details.js b/src/cabal-details.js index c3c5cf6..678cab3 100644 --- a/src/cabal-details.js +++ b/src/cabal-details.js @@ -688,8 +688,8 @@ class CabalDetails extends EventEmitter { this.users = new Map() Object.keys(users).forEach(key => { this.users[key] = new User(users[key]) - }) - this._initializeLocalUser(() => { + }) + this._initializeLocalUser(() => { loadModerationState(() => { this.registerListener(cabal.moderation.events, 'update', (info) => { let user = this.users[info.id] diff --git a/src/commands.js b/src/commands.js index c9f1d7c..0271bcb 100644 --- a/src/commands.js +++ b/src/commands.js @@ -328,8 +328,8 @@ module.exports = { actions: { help: () => 'print out a historic log of the moderation actions applied by you, and your active moderators & admins', call: (cabal, res, arg) => { - const promises = [cabal.moderation.getAdmins(), cabal.moderation.getMods()] - // get all moderation actions issued by our current mods & admins + const promises = [cabal.moderation.getAdmins(), cabal.moderation.getMods()] + // get all moderation actions issued by our current mods & admins const messages = [] function processMessages (messages) { res.info('moderation actions') @@ -341,7 +341,7 @@ module.exports = { res.info(message.text) }) } - Promise.all(promises).then(results => { + Promise.all(promises).then(results => { const keys = results[0].concat(results[1]) listNextKey() function listNextKey () { @@ -350,7 +350,7 @@ module.exports = { return res.end() } var key = keys.shift() - const write = (row, enc, next) => { + const write = (row, enc, next) => { if (!row) return const name = cabal.users[key] ? cabal.users[key].name : key.slice(0, 8) const target = cabal.users[row.content.id] ? cabal.users[row.content.id].name : row.content.id.slice(0, 8) @@ -362,18 +362,18 @@ module.exports = { if (['admin', 'mod'].includes(role)) { action = (type === 'add' ? 'added' : 'removed') } if (role === 'hide') { action = (type === 'add' ? 'hid' : 'unhid') } if (role === 'hide') { - text = `${datestr} ${name} ${action} ${target} ${reason}` + text = `${datestr} ${name} ${action} ${target} ${reason}` } else { - text = `${datestr} ${name} ${action} ${target} as ${role} ${reason}` + text = `${datestr} ${name} ${action} ${target} as ${role} ${reason}` } messages.push({ text, timestamp: parseFloat(row.timestamp) }) next() - } - const end = (next) => { + } + const end = (next) => { listNextKey() next() } - pump(cabal.core.moderation.listModerationBy(key), to.obj(write, end)) + pump(cabal.core.moderation.listModerationBy(key), to.obj(write, end)) } }) } @@ -381,13 +381,13 @@ module.exports = { roles: { help: () => 'list all your current moderators and admins', call: (cabal, res, arg) => { - const promises = [cabal.moderation.getAdmins(), cabal.moderation.getMods()] - Promise.all(promises).then(results => { + const promises = [cabal.moderation.getAdmins(), cabal.moderation.getMods()] + Promise.all(promises).then(results => { const keys = results[0].concat(results[1]) const print = (type) => { - return (k) => { + return (k) => { res.info(`${cabal.users[k] ? cabal.users[k].name : k.slice(0, 8)}: ${type}`) - } + } } res.info('moderation roles') if (keys.length === 1 && keys[0] === cabal.getLocalUser().key) { @@ -400,7 +400,7 @@ module.exports = { results[0].map(printAdmins) results[1].map(printMods) res.end() - }) + }) } }, inspect: { @@ -588,7 +588,7 @@ function flagCmd (cmd, cabal, res, arg) { var reason = args.slice(1).join(' ') const reasonstr = reason ? '(reason: ' + reason + ')' : '' cabal.moderation.setFlag(flag, type, channel, id, reason).then(() => { - if (['admin', 'mod'].includes(flag)) { + if (['admin', 'mod'].includes(flag)) { if (/^un/.test(cmd) && flag === 'mod' && !cabal.users[id].isModerator()) { res.error(`${getPeerName(cabal, id)} is not a mod`) } else if (/^un/.test(cmd) && flag === 'admin' && !cabal.users[id].isAdmin()) { @@ -598,7 +598,7 @@ function flagCmd (cmd, cabal, res, arg) { } else if (!/^un/.test(cmd) && flag === 'admin' && cabal.users[id].isAdmin()) { res.error(`${getPeerName(cabal, id)} is already an admin`) } - } else { + } else { if (/^un/.test(cmd)) { if (!cabal.users[id].isHidden()) { res.error(`cannot unhide ${getPeerName(cabal, id)}: they are not hidden`) @@ -608,7 +608,7 @@ function flagCmd (cmd, cabal, res, arg) { res.error(`${getPeerName(cabal, id)} is already hidden`) } } - } + } res.end() }).catch((err) => { res.error(err) }) } diff --git a/src/moderation.js b/src/moderation.js index c5f7324..c877470 100644 --- a/src/moderation.js +++ b/src/moderation.js @@ -65,14 +65,14 @@ class Moderation { setFlag (flag, type, channel = '@', id, reason = '') { // a list of [[id, reason]] was passed in if (typeof id === 'object' && typeof id[Symbol.iterator] === 'function') { - const promises = id.map((entry) => { + const promises = id.map((entry) => { return new Promise((resolve, reject) => { - this._flagCmd(flag, type, channel, entry[0], entry[1], (err) => { + this._flagCmd(flag, type, channel, entry[0], entry[1], (err) => { if (err) { return reject(err) } else { resolve() } - }) + }) }) - }) - return Promise.all(promises) + }) + return Promise.all(promises) } return new Promise((resolve, reject) => { this._flagCmd(flag, type, channel, id, reason, (err) => { From ee626cd2bc667ef191e94044b6021465d36bac17 Mon Sep 17 00:00:00 2001 From: substack Date: Sat, 4 Jul 2020 18:35:37 -1000 Subject: [PATCH 2/2] refactor for blocking and additional flags. disconnect immediately to blocked peers --- package.json | 3 +- src/cabal-details.js | 4 ++ src/client.js | 12 ++++- src/commands.js | 116 ++++++++++++++++++++++++++++--------------- 4 files changed, 92 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index f3f2fdd..2b192d0 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "qrcode": "^1.4.4", "random-access-memory": "^3.1.1", "strftime": "^0.10.0", - "to2": "^1.0.0" + "to2": "^1.0.0", + "uniq": "^1.0.1" }, "devDependencies": { "jsdoc-to-markdown": "^5.0.2", diff --git a/src/cabal-details.js b/src/cabal-details.js index 678cab3..6dbaac4 100644 --- a/src/cabal-details.js +++ b/src/cabal-details.js @@ -719,6 +719,10 @@ class CabalDetails extends EventEmitter { const changeOccurred = Object.keys(changedRole).filter(r => changedRole[r]).length > 0 if (!changeOccurred) { this._emitUpdate('user-updated', { key: info.id, user }) + if ((user.flags.get('@') || []).includes('block')) { + // drop the connection to blocked users immediately + this.core.removeConnection(info.id) + } return } const type = doc.type.replace(/^flags\//, '') diff --git a/src/client.js b/src/client.js index 4dd9cbd..6021f82 100644 --- a/src/client.js +++ b/src/client.js @@ -191,7 +191,17 @@ class Client { aliases: this.aliases }, done) this.cabals.set(cabal, details) - if (!opts.noSwarm) cabal.swarm() + if (!opts.noSwarm) cabal.swarm({ + verify: function (remotePubKey, cb) { + cabal.moderation.getFlags({ + id: remotePubKey.toString('hex'), + channel: '@' + }, function (err, flags) { + if (err) cb(err) + else cb(null, !flags.includes('block')) + }) + } + }) function done () { details._emitUpdate('init') cb() diff --git a/src/commands.js b/src/commands.js index 0271bcb..a4156bf 100644 --- a/src/commands.js +++ b/src/commands.js @@ -2,6 +2,7 @@ const qr = require('qrcode') const pump = require('pump') const to = require('to2') const strftime = require('strftime') +const uniq = require('uniq') module.exports = { add: { @@ -328,8 +329,8 @@ module.exports = { actions: { help: () => 'print out a historic log of the moderation actions applied by you, and your active moderators & admins', call: (cabal, res, arg) => { - const promises = [cabal.moderation.getAdmins(), cabal.moderation.getMods()] - // get all moderation actions issued by our current mods & admins + const promises = [cabal.moderation.getAdmins(), cabal.moderation.getMods()] + // get all moderation actions issued by our current mods & admins const messages = [] function processMessages (messages) { res.info('moderation actions') @@ -341,7 +342,7 @@ module.exports = { res.info(message.text) }) } - Promise.all(promises).then(results => { + Promise.all(promises).then(results => { const keys = results[0].concat(results[1]) listNextKey() function listNextKey () { @@ -350,30 +351,30 @@ module.exports = { return res.end() } var key = keys.shift() - const write = (row, enc, next) => { + const write = (row, enc, next) => { if (!row) return const name = cabal.users[key] ? cabal.users[key].name : key.slice(0, 8) const target = cabal.users[row.content.id] ? cabal.users[row.content.id].name : row.content.id.slice(0, 8) const type = row.type.split('/')[1] const reason = row.content.reason - const role = row.content.flags[0] - const datestr = strftime('[%F %T] ', new Date(row.timestamp)) - let text, action - if (['admin', 'mod'].includes(role)) { action = (type === 'add' ? 'added' : 'removed') } - if (role === 'hide') { action = (type === 'add' ? 'hid' : 'unhid') } - if (role === 'hide') { - text = `${datestr} ${name} ${action} ${target} ${reason}` - } else { - text = `${datestr} ${name} ${action} ${target} as ${role} ${reason}` - } + const flags = row.content.flags + const datestr = strftime('[%F %T]', new Date(row.timestamp)) + const action = { + add: 'added', + remove: 'removed', + set: 'set' + }[type] + text = `${datestr} ${name} ${ + formatAction({ type, flags, id: target, cabal}) + } ${reason ? ': ' + reason : ''}` messages.push({ text, timestamp: parseFloat(row.timestamp) }) next() - } - const end = (next) => { + } + const end = (next) => { listNextKey() next() } - pump(cabal.core.moderation.listModerationBy(key), to.obj(write, end)) + pump(cabal.core.moderation.listModerationBy(key), to.obj(write, end)) } }) } @@ -385,9 +386,9 @@ module.exports = { Promise.all(promises).then(results => { const keys = results[0].concat(results[1]) const print = (type) => { - return (k) => { + return (k) => { res.info(`${cabal.users[k] ? cabal.users[k].name : k.slice(0, 8)}: ${type}`) - } + } } res.info('moderation roles') if (keys.length === 1 && keys[0] === cabal.getLocalUser().key) { @@ -586,31 +587,34 @@ function flagCmd (cmd, cabal, res, arg) { var type = /^un/.test(cmd) ? 'remove' : 'add' var flag = cmd.replace(/^un/, '') var reason = args.slice(1).join(' ') - const reasonstr = reason ? '(reason: ' + reason + ')' : '' - cabal.moderation.setFlag(flag, type, channel, id, reason).then(() => { - if (['admin', 'mod'].includes(flag)) { - if (/^un/.test(cmd) && flag === 'mod' && !cabal.users[id].isModerator()) { - res.error(`${getPeerName(cabal, id)} is not a mod`) - } else if (/^un/.test(cmd) && flag === 'admin' && !cabal.users[id].isAdmin()) { - res.error(`${getPeerName(cabal, id)} is not an admin`) - } else if (!/^un/.test(cmd) && flag === 'mod' && cabal.users[id].isModerator()) { - res.error(`${getPeerName(cabal, id)} is already a mod`) - } else if (!/^un/.test(cmd) && flag === 'admin' && cabal.users[id].isAdmin()) { - res.error(`${getPeerName(cabal, id)} is already an admin`) - } + cabal.core.moderation.getFlags({ id, channel }, function (err, flags) { + if (err) return res.error(err) + if (type === 'remove' && !flags.includes(flag)) { + res.error(`user ${getPeerName(cabal, id)} does not have the flag: ${flag}`) + } else if (type === 'add' && flags.includes(flag)) { + res.error(`user ${getPeerName(cabal, id)} already has the flag: ${flag}`) } else { - if (/^un/.test(cmd)) { - if (!cabal.users[id].isHidden()) { - res.error(`cannot unhide ${getPeerName(cabal, id)}: they are not hidden`) - } - } else { - if (cabal.users[id].isHidden()) { - res.error(`${getPeerName(cabal, id)} is already hidden`) - } + if (type === 'add') { + flags.push(flag) + uniq(flags) + } else if (type === 'remove') { + uniq(flags) + var i = flags.indexOf(flag) + if (i >= 0) flags.splice(i, 1) } + var fname = { add: 'addFlags', remove: 'removeFlags' }[type] + cabal.core.moderation[fname]({ + id, + channel, + flags: [flag], + reason + }, function (err) { + if (err) return res.error(err) + //res.info(formatAction({ type, flags: [flag], id, cabal })) + res.end() + }) } - res.end() - }).catch((err) => { res.error(err) }) + }) } function listCmd (cmd, cabal, res, arg) { @@ -645,3 +649,33 @@ function listCmd (cmd, cabal, res, arg) { function ucfirst (s) { return s.replace(/^[a-z]/, function (c) { return c.toUpperCase() }) } + +function formatAction ({ cabal, type, flags, id }) { + var verb = { + add: 'added', + remove: 'removed', + set: 'set' + }[type] + if (type === 'set') { + return `${verb} flags [${flags}] for user ${getPeerName(cabal, id)}` + } else if (type === 'add' && flags.length === 1 && flags[0] === 'hide') { + return `hid user ${getPeerName(cabal, id)}` + } else if (type === 'remove' && flags.length === 1 && flags[0] === 'hide') { + return `unhid user ${getPeerName(cabal, id)}` + } else if (type === 'add' && flags.length === 1 && flags[0] === 'block') { + return `blocked user ${getPeerName(cabal, id)}` + } else if (type === 'remove' && flags.length === 1 && flags[0] === 'block') { + return `unblocked user ${getPeerName(cabal, id)}` + } else if (type === 'add' && flags.length === 1 && flags[0] === 'admin') { + return `added user ${getPeerName(cabal, id)} as admin` + } else if (type === 'remove' && flags.length === 1 && flags[0] === 'admin') { + return `removed user ${getPeerName(cabal, id)} as admin` + } else if (type === 'add' && flags.length === 1 && flags[0] === 'mod') { + return `added user ${getPeerName(cabal, id)} as mod` + } else if (type === 'remove' && flags.length === 1 && flags[0] === 'mod') { + return `removed user ${getPeerName(cabal, id)} as mod` + } else { + var flag = flags.length === 1 ? 'flag ' + flags[0] : 'flags ['+flags+']' + return `${verb} ${flag} for user ${getPeerName(cabal, id)}` + } +}