Skip to content

Commit

Permalink
Merge pull request #95 from ssbc/listmembers-exclusion
Browse files Browse the repository at this point in the history
Adapt listMembers to exclusion and test it
  • Loading branch information
Powersource authored May 11, 2023
2 parents e3591f6 + 2fd0c34 commit 12e9979
Show file tree
Hide file tree
Showing 12 changed files with 935 additions and 400 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,22 @@ Publishes any kind of message encrypted to the group. The function wraps `ssb.db

### `ssb.tribes2.listMembers(groupId, { live }) => source`

Returns a pull stream source listing every known member of the group with id `groupId`. Note: lists members whether or not they've accepted the invite. If `live` is true, then it keeps the stream open and also outputs new members.
Returns a pull stream source listing the root feed id of every member of the
group with id `groupId`. Note: lists members whether or not they've accepted the
invite.

If `live` is true, then it keeps the stream open and also outputs updates to
membership as new members are added / excluded.

Each update emitted from the source is the updated complete state for the
current preferred epoch of the group in the format:

```js
{
added: [feedId, feedId, ...],
toExclude: [feedId, ...]
}
```

### `ssb.tribes2.listInvites() => source`

Expand Down
168 changes: 48 additions & 120 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ const { promisify } = require('util')
const pull = require('pull-stream')
const paraMap = require('pull-paramap')
const pullMany = require('pull-many')
const pullFlatMerge = require('pull-flat-merge')
const pullAbortable = require('pull-abortable')
const pullDefer = require('pull-defer')
const chunk = require('lodash.chunk')
const clarify = require('clarify-error')
const {
where,
and,
or,
isDecrypted,
type,
live,
toPullStream,
} = require('ssb-db2/operators')
const {
Expand All @@ -30,12 +32,14 @@ const {
} = require('private-group-spec')
const { SecretKey } = require('ssb-private-group-keys')
const { fromMessageSigil, isBendyButtV1FeedSSBURI } = require('ssb-uri2')

const startListeners = require('./listeners')
const buildGroupId = require('./lib/build-group-id')
const addTangles = require('./lib/tangles/add-tangles')
const publishAndPrune = require('./lib/prune-publish')
const MetaFeedHelpers = require('./lib/meta-feed-helpers')
const Epochs = require('./lib/epochs')
const { groupRecp } = require('./lib/operators')
// const Epochs = require('./lib/epochs')

module.exports = {
name: 'tribes2',
Expand All @@ -60,7 +64,7 @@ module.exports = {
findOrCreateGroupWithoutMembers,
getRootFeedIdFromMsgId,
} = MetaFeedHelpers(ssb)
// const { getEpochs } = Epochs(ssb)
const { getPreferredEpoch, getMembers } = Epochs(ssb)

function create(opts = {}, cb) {
if (cb === undefined) return promisify(create)(opts)
Expand Down Expand Up @@ -208,6 +212,8 @@ module.exports = {

pull(
listMembers(groupId),
pull.map((info) => info.added),
pull.flatten(),
pull.collect((err, beforeMembers) => {
// prettier-ignore
if (err) return cb(clarify(err, "Couldn't get old member list when excluding members"))
Expand Down Expand Up @@ -255,8 +261,7 @@ module.exports = {
pull.collect((err) => {
// prettier-ignore
if (err) return cb(clarify(err, "Couldn't re-add remaining members when excluding members"))

return cb()
cb(null)
})
)
})
Expand Down Expand Up @@ -319,43 +324,54 @@ module.exports = {
}

function listMembers(groupId, opts = {}) {
const deferedSource = pullDefer.source()
const { live } = opts
const deferredSource = pullDefer.source()

get(groupId, (err, group) => {
// prettier-ignore
if (err) return deferedSource.abort(clarify(err, 'Failed to get group info when listing members'))
if (err) return deferredSource.abort(clarify(err, 'Failed to get group info when listing members'))
// prettier-ignore
if (group.excluded) return deferedSource.abort( new Error("We're excluded from this group, can't list members"))
if (group.excluded) return deferredSource.abort( new Error("We're excluded from this group, can't list members"))

if (!live) {
getPreferredEpoch(groupId, (err, epoch) => {
// prettier-ignore
if (err) return deferredSource.abort(clarify(err, 'failed to load preferred epoch'))

getMembers(epoch.id, (err, res) => {
// prettier-ignore
if (err) return deferredSource.abort(clarify(err, 'error getting members'))

const source = pull.once(res)
deferredSource.resolve(source)
})
})
return
}

let abortable = pullAbortable()
const source = pull(
ssb.db.query(
where(
and(
isDecrypted('box2'),
type('group/add-member'),
groupRecp(groupId)
)
),
opts.live ? live({ old: true }) : null,
toPullStream()
),
pull.map((msg) => msg.value.content.recps.slice(1)),
pull.flatten(),
pull.unique()
)
getPreferredEpoch.stream(groupId, { live }),
pull.map((epoch) => {
abortable.abort()
abortable = pullAbortable()

deferedSource.resolve(source)
return pull(getMembers.stream(epoch.id, { live }), abortable)
}),
pullFlatMerge()
)
deferredSource.resolve(source)
})

return deferedSource
return deferredSource
}

function listInvites() {
const deferedSource = pullDefer.source()
const deferredSource = pullDefer.source()

getMyGroups((err, myGroups) => {
// prettier-ignore
if (err) return deferedSource.abort(clarify(err, 'Failed to list group IDs when listing invites'))
if (err) return deferredSource.abort(clarify(err, 'Failed to list group IDs when listing invites'))

const source = pull(
// get all the groupIds we've heard of from invites
Expand All @@ -374,17 +390,18 @@ module.exports = {
pull.asyncMap(getGroupInviteData)
)

deferedSource.resolve(source)
deferredSource.resolve(source)
})

return deferedSource
return deferredSource

// listInvites helpers

function getMyGroups(cb) {
const myGroups = new Set()

pull(
// TODO replace with pull.values (unless want "round-robbin" sampling)
pullMany([
ssb.box2.listGroupIds(),
ssb.box2.listGroupIds({ excluded: true }),
Expand Down Expand Up @@ -481,97 +498,8 @@ module.exports = {
findOrCreateAdditionsFeed((err) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Error finding or creating additions feed when starting ssb-tribes2'))
return cb()
})

ssb.metafeeds.findOrCreate((err, myRoot) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Error getting own root in start()'))

// check if we've been excluded
pull(
ssb.db.query(
where(and(isDecrypted('box2'), type('group/exclude-member'))),
live({ old: true }),
toPullStream()
),
pull.filter(isExcludeMember),
pull.filter((msg) =>
// it's an exclusion of us
msg.value.content.excludes.includes(myRoot.id)
),
pull.drain(
(msg) => {
const groupId = msg.value.content.recps[0]
ssb.box2.excludeGroupInfo(groupId, (err) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Error on excluding group info after finding exclusion of ourselves'))
})
},
(err) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Error on looking for exclude messages excluding us'))
}
)
)

// look for new epochs that we're added to
pull(
ssb.db.query(
where(and(isDecrypted('box2'), type('group/add-member'))),
live({ old: true }),
toPullStream()
),
pull.filter(isAddMember),
// groups/epochs we're added to
pull.filter((msg) => {
return msg.value.content.recps.includes(myRoot.id)
}),
// to find new epochs we only check groups we've accepted the invite to
paraMap((msg, cb) => {
pull(
ssb.box2.listGroupIds(),
pull.filter((groupId) => groupId === msg.value.content.recps[0]),
pull.take(1),
pull.collect((err, groupIds) => {
// prettier-ignore
if (err) return cb(clarify(err, "Error getting groups we're already in when looking for new epochs"))
cb(null, groupIds.length ? msg : null)
})
)
}, 4),
pull.filter(Boolean),
pull.drain(
(msg) => {
const groupId = msg.value.content.recps[0]

const newKey = Buffer.from(msg.value.content.groupKey, 'base64')
ssb.box2.addGroupInfo(groupId, { key: newKey }, (err) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Error adding new epoch key that we found'))

const newKeyPick = {
key: newKey,
scheme: keySchemes.private_group,
}
// TODO: naively guessing that this is the latest key for now
ssb.box2.pickGroupWriteKey(groupId, newKeyPick, (err) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Error switching to new epoch key that we found'))

ssb.db.reindexEncrypted((err) => {
// prettier-ignore
if (err) cb(clarify(err, 'Error reindexing after finding new epoch'))
})
})
})
},
(err) => {
// prettier-ignore
if (err) return cb(clarify(err, "Error finding new epochs we've been added to"))
}
)
)
cb(null)
startListeners(ssb, getPreferredEpoch, console.error)
})
}

Expand Down
Loading

0 comments on commit 12e9979

Please sign in to comment.