Commit 765a31b854726f3c3f4e39689b4e4d4b8f86bcf8
Merge branch 'friends2' into flume
Dominic Tarr committed on 5/28/2017, 10:48:09 AMParent: d347cecf5f54a7f0f690fd6c780b3bb092875388
Parent: 3c54223e55ec3292754f1ff70a4176dd7e0d7083
Files changed
package.json | changed |
plugins/block.js | changed |
plugins/friends.js | changed |
test/block.js | changed |
test/block2.js | changed |
test/block3.js | changed |
package.json | ||
---|---|---|
@@ -55,8 +55,9 @@ | ||
55 | 55 | "ssb-blobs": "^1.0.1", |
56 | 56 | "ssb-client": "^4.4.1", |
57 | 57 | "ssb-config": "^2.0.0", |
58 | 58 | "ssb-ebt": "0.0.7", |
59 | + "ssb-friends": "^1.0.0", | |
59 | 60 | "ssb-keys": "^7.0.0", |
60 | 61 | "ssb-links": "^3.0.0", |
61 | 62 | "ssb-msgs": "~5.2.0", |
62 | 63 | "ssb-query": "^1.0.0", |
plugins/block.js | ||
---|---|---|
@@ -12,30 +12,21 @@ | ||
12 | 12 | //TODO: move other blocking code in here, |
13 | 13 | // i think we'll need a hook system for this. |
14 | 14 | |
15 | 15 | //if a currently connected peer is blocked, disconnect them immediately. |
16 | - pull( | |
17 | - sbot.friends.createFriendStream({graph: 'flag', live: true}), | |
18 | - pull.drain(function (blocked) { | |
19 | - if(sbot.peers[blocked]) { | |
20 | - sbot.peers[blocked].forEach(function (b) { | |
21 | - b.close(true, function () {}) | |
22 | - }) | |
23 | - } | |
24 | - }) | |
25 | - ) | |
26 | 16 | |
17 | + var g = {} | |
18 | + | |
19 | + sbot.friends.post(function (_g) { | |
20 | + g = _g | |
21 | + }) | |
22 | + | |
27 | 23 | function isBlocked (_opts) { |
28 | 24 | var opts |
29 | - | |
25 | + if(!g) return //only possible briefly at startup | |
30 | 26 | if('string' === typeof _opts) |
31 | - opts = { | |
32 | - source: sbot.id, dest: _opts, graph:'flag' | |
33 | - } | |
34 | - else opts = { | |
35 | - source: _opts.source, dest: _opts.dest, graph: 'flag' | |
36 | - } | |
37 | - return sbot.friends.get(opts) | |
27 | + return g[sbot.id] ? g[sbot.id][_opts] === false : false | |
28 | + return g[_opts.source] ? g[_opts.source][_opts.dest] === false : false | |
38 | 29 | } |
39 | 30 | |
40 | 31 | sbot.createHistoryStream.hook(function (fn, args) { |
41 | 32 | var opts = args[0], id = this.id |
@@ -51,9 +42,9 @@ | ||
51 | 42 | if(!msg.content && msg.value.content) |
52 | 43 | msg = msg.value |
53 | 44 | if(msg.content.type !== 'contact') return true |
54 | 45 | return !( |
55 | - msg.content.flagged && | |
46 | + (msg.content.flagged || msg.content.blocking) && | |
56 | 47 | msg.content.contact === id |
57 | 48 | ) |
58 | 49 | }) |
59 | 50 | ) |
plugins/friends.js | ||
---|---|---|
@@ -7,9 +7,13 @@ | ||
7 | 7 | var mdm = require('mdmanifest') |
8 | 8 | var valid = require('../lib/validators') |
9 | 9 | var apidoc = require('../lib/apidocs').friends |
10 | 10 | var ref = require('ssb-ref') |
11 | +var Obv = require('obv') | |
11 | 12 | |
13 | +var F = require('ssb-friends') | |
14 | +var block = require('ssb-friends/block') | |
15 | + | |
12 | 16 | // friends plugin |
13 | 17 | // methods to analyze the social graph |
14 | 18 | // maintains a 'follow' and 'flag' graph |
15 | 19 | |
@@ -29,26 +33,38 @@ | ||
29 | 33 | exports.version = '1.0.0' |
30 | 34 | exports.manifest = mdm.manifest(apidoc) |
31 | 35 | |
32 | 36 | exports.init = function (sbot, config) { |
33 | - var g = {} | |
34 | - var index = sbot._flumeUse('friends', Reduce(1, function (_, rel) { | |
35 | - //if(!g) g = {} | |
36 | - if(!ref.isFeed(rel.from)) throw new Error('FROM is not id') | |
37 | + var post = Obv() | |
38 | + post.set({}) | |
39 | + var index = sbot._flumeUse('friends', Reduce(2, function (g, rel) { | |
40 | + if(!g) g = {} | |
37 | 41 | G.addEdge(g, rel.from, rel.to, rel.value) |
38 | 42 | return g |
39 | 43 | }, function (data) { |
40 | 44 | if(data.value.content.type === 'contact' && ref.isFeed(data.value.content.contact)) { |
45 | + var tristate = ( | |
46 | + data.value.content.following ? true | |
47 | + : data.value.content.flagged || data.value.content.blocking ? false | |
48 | + : null | |
49 | + ) | |
41 | 50 | return { |
42 | 51 | from: data.value.author, |
43 | 52 | to: data.value.content.contact, |
44 | - value: data.value.content.following | |
53 | + value: tristate | |
45 | 54 | } |
46 | 55 | } |
47 | 56 | })) |
48 | 57 | |
58 | + index.since(function () { | |
59 | + //it looks async but this will always be sync after loading | |
60 | + index.get(null, function (_, v) { | |
61 | + post.set(v) | |
62 | + }) | |
63 | + }) | |
64 | + | |
49 | 65 | return { |
50 | - | |
66 | + post: post, | |
51 | 67 | get: function (opts, cb) { |
52 | 68 | index.get(opts, cb) |
53 | 69 | }, |
54 | 70 | |
@@ -56,12 +72,9 @@ | ||
56 | 72 | opts = opts || {} |
57 | 73 | var live = opts.live === true |
58 | 74 | var meta = opts.meta === true |
59 | 75 | var start = opts.start || sbot.id |
60 | - var first = true | |
61 | 76 | var reachable |
62 | - if(!g) throw new Error('not initialized') | |
63 | - //g = g || {} | |
64 | 77 | return pull( |
65 | 78 | index.stream(opts), |
66 | 79 | FlatMap(function (v) { |
67 | 80 | if(!v) return [] |
@@ -69,44 +82,28 @@ | ||
69 | 82 | //this code handles real time streaming of the hops map. |
70 | 83 | function push (to, hops) { |
71 | 84 | out.push(meta ? {id: to, hops: hops} : to) |
72 | 85 | } |
73 | - var out = [] | |
74 | - if(v.from && v.to) { | |
75 | - if(!reachable) { | |
76 | - //this is is hack... | |
77 | - reachable = {} | |
78 | - reachable[sbot.id] = 0 | |
79 | - push(sbot.id, 0) | |
80 | - } | |
81 | - //recalculate the portion of the graph, reachable in opts.hops | |
82 | - //(but only the portion not already reachable) | |
83 | - var _reachable = G.hops(g, v.from, reachable[v.from], opts.hops || 3, reachable) | |
84 | 86 | |
85 | - for(var k in _reachable) { | |
86 | - //check if it has _become_ reachable just now. | |
87 | - //if so add to the set | |
88 | - if(reachable[k] == null) | |
89 | - push(k, reachable[k] = _reachable[k]) | |
90 | - //if this has shortened the path, then update. | |
91 | - else if(reachable[k] > _reachable[k]) | |
92 | - reachable[k] = _reachable[k] | |
93 | - //else, we where already able to reach this node. | |
94 | - } | |
95 | - } | |
96 | - else { | |
97 | - var _g = v | |
98 | - reachable = G.hops(_g, start, 0, opts.hops || 3) | |
87 | + var out = [], g = post.value | |
88 | + | |
89 | + //the edge has already been added to g | |
90 | + if(!reachable) { | |
91 | + reachable = F.reachable(g, start, block) | |
99 | 92 | for(var k in reachable) |
100 | - push(k, reachable[k]) | |
101 | - } | |
102 | - if(first) { | |
103 | - first = false | |
104 | - if(live) { | |
105 | - out.push({sync: true}) | |
93 | + if(block.isWanted(reachable[k])) | |
94 | + push(k, reachable[k][0]) | |
95 | + } else { | |
96 | + var _reachable = F.reachable(g, start, block) | |
97 | + var patch = F.diff(reachable, _reachable) | |
98 | + for(var k in patch) { | |
99 | + if(patch[k] == null) | |
100 | + push(k, -1) | |
101 | + else if(block.isWanted(patch[k])) | |
102 | + push(k, patch[k][0]) | |
106 | 103 | } |
104 | + reachable = _reachable | |
107 | 105 | } |
108 | - | |
109 | 106 | return out |
110 | 107 | }) |
111 | 108 | |
112 | 109 | ) |
@@ -126,4 +123,7 @@ | ||
126 | 123 | } |
127 | 124 | } |
128 | 125 | |
129 | 126 | |
127 | + | |
128 | + | |
129 | + |
test/block.js | ||
---|---|---|
@@ -10,8 +10,16 @@ | ||
10 | 10 | .use(require('../plugins/replicate')) |
11 | 11 | |
12 | 12 | var toAddress = require('../lib/util').toAddress |
13 | 13 | |
14 | +function once (fn) { | |
15 | + var called = 0 | |
16 | + return function () { | |
17 | + if(called++) throw new Error('called :'+called+' times!') | |
18 | + return fn.apply(this, arguments) | |
19 | + } | |
20 | +} | |
21 | + | |
14 | 22 | // alice, bob, and carol all follow each other, |
15 | 23 | // but then bob offends alice, and she blocks him. |
16 | 24 | // this means that: |
17 | 25 | // |
@@ -35,16 +43,21 @@ | ||
35 | 43 | }) |
36 | 44 | |
37 | 45 | tape('alice blocks bob, and bob cannot connect to alice', function (t) { |
38 | 46 | |
47 | + console.log({ | |
48 | + alice: alice.id, | |
49 | + bob: bob.id, | |
50 | + carol: carol.id | |
51 | + }) | |
52 | + | |
39 | 53 | //in the beginning alice and bob follow each other |
40 | 54 | cont.para([ |
41 | 55 | cont(alice.publish)(u.follow(bob.id)), |
42 | 56 | cont(bob .publish)(u.follow(alice.id)), |
43 | 57 | cont(carol.publish)(u.follow(alice.id)) |
44 | 58 | ]) (function (err) { |
45 | 59 | if(err) throw err |
46 | - | |
47 | 60 | var n = 3, rpc |
48 | 61 | |
49 | 62 | bob.connect(alice.getAddress(), function (err, _rpc) { |
50 | 63 | if(err) throw err |
@@ -54,24 +67,23 @@ | ||
54 | 67 | }) |
55 | 68 | |
56 | 69 | //get the next messages that are replicated to alice and bob, |
57 | 70 | //and check that these are the correct follow messages. |
58 | - var bobCancel = bob.post(function (op) { | |
71 | + var bobCancel = bob.post(once(function (op) { | |
59 | 72 | console.log('BOB_POST', op) |
60 | 73 | //should be the alice's follow(bob) message. |
61 | - t.equal(op.value.author, alice.id) | |
62 | - t.equal(op.value.content.contact, bob.id) | |
74 | + t.equal(op.value.author, alice.id, 'bob expected message from alice') | |
75 | + t.equal(op.value.content.contact, bob.id, 'bob expected message to be about bob') | |
63 | 76 | next() |
64 | - }) | |
77 | + }), false) | |
65 | 78 | |
66 | - var aliceCancel = alice.post(function (op) { | |
79 | + var aliceCancel = alice.post(once(function (op) { | |
67 | 80 | console.log('ALICE_POST', op) |
68 | 81 | //should be the bob's follow(alice) message. |
69 | - t.equal(op.value.author, bob.id) | |
70 | - t.equal(op.value.content.contact, alice.id) | |
82 | + t.equal(op.value.author, bob.id, 'alice expected to receive a message from bob') | |
83 | + t.equal(op.value.content.contact, alice.id, 'alice expected received message to be about alice') | |
71 | 84 | next() |
72 | - }) | |
73 | - | |
85 | + }), false) | |
74 | 86 | function next () { |
75 | 87 | if(--n) return |
76 | 88 | |
77 | 89 | rpc.close(true, function () { |
@@ -82,68 +94,61 @@ | ||
82 | 94 | alice.publish(u.block(bob.id)) |
83 | 95 | (function (err) { |
84 | 96 | if(err) throw err |
85 | 97 | |
86 | - t.ok(alice.friends.get({source: alice.id, dest: bob.id, graph: 'flag'})) | |
98 | + alice.friends.get(null, function (err, g) { | |
99 | + if(err) throw err | |
100 | + t.equal(g[alice.id][bob.id], false) | |
87 | 101 | |
88 | - pull( | |
89 | - alice.links({ | |
90 | - source: alice.id, | |
91 | - dest: bob.id, | |
92 | - rel: 'contact', | |
93 | - values: true | |
94 | - }), | |
95 | - pull.filter(function (op) { | |
96 | - return op.value.content.flagged != null | |
97 | - }), | |
98 | - pull.collect(function (err, ary) { | |
99 | - if(err) throw err | |
100 | - console.log(ary) | |
101 | - t.ok(flagged = ary.pop().value.content.flagged, 'alice did block bob') | |
102 | + pull( | |
103 | + alice.links({ | |
104 | + source: alice.id, | |
105 | + dest: bob.id, | |
106 | + rel: 'contact', | |
107 | + values: true | |
108 | + }), | |
109 | + pull.filter(function (op) { | |
110 | + return op.value.content.flagged != null | |
111 | + }), | |
112 | + pull.collect(function (err, ary) { | |
113 | + if(err) throw err | |
114 | + console.log(ary) | |
115 | + t.ok(flagged = ary.pop().value.content.flagged, 'alice did block bob') | |
102 | 116 | |
103 | - //since bob is blocked, he should not be able to connect | |
104 | - bob.connect(alice.getAddress(), function (err, rpc) { | |
105 | - t.ok(err, 'bob is blocked, should fail to connect to alice') | |
117 | + //since bob is blocked, he should not be able to connect | |
118 | + bob.connect(alice.getAddress(), function (err, rpc) { | |
119 | + t.ok(err, 'bob is blocked, should fail to connect to alice') | |
106 | 120 | |
107 | 121 | |
108 | - carol.post(function (msg) { | |
109 | - console.log('CAROL RECV', msg, alice.id) | |
110 | - if(msg.author === alice.id) { | |
111 | - if(msg.sequence == 2) | |
112 | - t.end() | |
113 | - } | |
114 | - }) | |
122 | + carol.post(function (msg) { | |
123 | + console.log('CAROL RECV', msg, alice.id) | |
124 | + if(msg.author === alice.id) { | |
125 | + if(msg.sequence == 2) | |
126 | + t.end() | |
127 | + } | |
128 | + }) | |
115 | 129 | |
116 | - //but carol, should, because she is not blocked. | |
117 | - carol.connect(alice.getAddress(), function (err, rpc) { | |
118 | - if(err) throw err | |
119 | - console.log('CAROL CONNECTED TO ALICE', carol.id, alice.id) | |
120 | -// pull( | |
121 | -// alice.createHistoryStream({id: alice.id, seq: 0}), | |
122 | -// pull.collect(console.log) | |
123 | -// ) | |
130 | + //but carol, should, because she is not blocked. | |
131 | + carol.connect(alice.getAddress(), function (err, rpc) { | |
132 | + if(err) throw err | |
133 | + console.log('CAROL CONNECTED TO ALICE', carol.id, alice.id) | |
134 | + rpc.on('closed', function () { | |
135 | + pull( | |
136 | + carol.createHistoryStream({id: alice.id, seq: 0, live: false}), | |
137 | + pull.collect(function (err, ary) { | |
138 | + if(err) throw err | |
124 | 139 | |
125 | - rpc.on('closed', function () { | |
126 | - pull( | |
127 | - carol.createHistoryStream({id: alice.id, seq: 0, live: false}), | |
128 | - pull.collect(function (err, ary) { | |
129 | - if(err) throw err | |
130 | - | |
131 | - t.ok(ary.length, 'carol replicated data from alice') | |
132 | - console.log(alice.id, carol.id, err, ary) | |
133 | - t.end() | |
134 | - }) | |
135 | - ) | |
140 | + t.ok(ary.length, 'carol replicated data from alice') | |
141 | + console.log(alice.id, carol.id, err, ary) | |
142 | + t.end() | |
143 | + }) | |
144 | + ) | |
145 | + }) | |
136 | 146 | }) |
137 | 147 | }) |
138 | -// carol.once('replicate:finish', function (vclock) { | |
139 | -// t.equal(vclock[alice.id], 2) | |
140 | -// //in next test, bob connects to carol... | |
141 | -// t.end() | |
142 | -// }) | |
143 | 148 | }) |
144 | - }) | |
145 | - ) | |
149 | + ) | |
150 | + }) | |
146 | 151 | }) |
147 | 152 | }) |
148 | 153 | } |
149 | 154 | }) |
@@ -177,7 +182,4 @@ | ||
177 | 182 | |
178 | 183 | |
179 | 184 | |
180 | 185 | |
181 | - | |
182 | - | |
183 | - |
Built with git-ssb-web