Files: 4e87fb7f772146e90bc4f79a30bed7079d748a46 / lib / friends-with-gossip-priority.js
4811 bytesRaw
1 | var Graphmitter = require('graphmitter') |
2 | var pull = require('pull-stream') |
3 | var mlib = require('ssb-msgs') |
4 | var memview = require('level-memview') |
5 | var pushable = require('pull-pushable') |
6 | var mdm = require('mdmanifest') |
7 | var valid = require('scuttlebot/lib/validators') |
8 | var apidoc = require('scuttlebot/lib/apidocs').friends |
9 | |
10 | // friends plugin |
11 | // methods to analyze the social graph |
12 | // maintains a 'follow' and 'flag' graph |
13 | |
14 | function isFunction (f) { |
15 | return 'function' === typeof f |
16 | } |
17 | |
18 | function isString (s) { |
19 | return 'string' === typeof s |
20 | } |
21 | |
22 | function isFriend (friends, a, b) { |
23 | return friends[a] && friends[b] && friends[a][b] && friends[b][a] |
24 | } |
25 | |
26 | exports.name = 'friends' |
27 | exports.version = '1.0.0' |
28 | exports.manifest = mdm.manifest(apidoc) |
29 | |
30 | exports.init = function (sbot, config) { |
31 | |
32 | var graphs = { |
33 | follow: new Graphmitter(), |
34 | flag: new Graphmitter() |
35 | } |
36 | |
37 | // view processor |
38 | var syncCbs = [] |
39 | function awaitSync (cb) { |
40 | if (syncCbs) syncCbs.push(cb) |
41 | else cb() |
42 | } |
43 | |
44 | // read/watch the log for changes to the social graph |
45 | pull(sbot.createLogStream({ live: true }), pull.drain(function (msg) { |
46 | |
47 | if (msg.sync) { |
48 | syncCbs.forEach(function (cb) { cb() }) |
49 | syncCbs = null |
50 | |
51 | if (sbot.gossip) { |
52 | // prioritize friends |
53 | var friends = graphs['follow'].toJSON() |
54 | sbot.gossip.peers().forEach(function(peer) { |
55 | if (isFriend(friends, sbot.id, peer.key)) { |
56 | sbot.gossip.add(peer, 'friends') |
57 | } |
58 | }) |
59 | } |
60 | |
61 | return |
62 | } |
63 | |
64 | var c = msg.value.content |
65 | if (c.type == 'contact') { |
66 | mlib.asLinks(c.contact, 'feed').forEach(function (link) { |
67 | if ('following' in c) { |
68 | if (c.following) |
69 | graphs.follow.edge(msg.value.author, link.link, true) |
70 | else |
71 | graphs.follow.del(msg.value.author, link.link) |
72 | |
73 | } |
74 | if ('flagged' in c) { |
75 | if (c.flagged) |
76 | graphs.flag.edge(msg.value.author, link.link, c.flagged) |
77 | else |
78 | graphs.flag.del(msg.value.author, link.link) |
79 | } |
80 | }) |
81 | } |
82 | })) |
83 | |
84 | return { |
85 | |
86 | get: valid.sync(function (opts) { |
87 | var g = graphs[opts.graph || 'follow'] |
88 | if(!g) throw new Error('opts.graph must be provided') |
89 | return g.get(opts.source, opts.dest) |
90 | }, 'object?'), |
91 | |
92 | all: valid.async(function (graph, cb) { |
93 | if (typeof graph == 'function') { |
94 | cb = graph |
95 | graph = null |
96 | } |
97 | if (!graph) |
98 | graph = 'follow' |
99 | awaitSync(function () { |
100 | cb(null, graphs[graph] ? graphs[graph].toJSON() : null) |
101 | }) |
102 | }, 'string?'), |
103 | |
104 | path: valid.sync(function (opts) { |
105 | if(isString(opts)) |
106 | opts = {source: sbot.id, dest: opts} |
107 | return graphs.follow.path(opts) |
108 | |
109 | }, 'string|object'), |
110 | |
111 | createFriendStream: valid.source(function (opts) { |
112 | opts = opts || {} |
113 | var live = opts.live === true |
114 | var meta = opts.meta === true |
115 | var start = opts.start || sbot.id |
116 | var graph = graphs[opts.graph || 'follow'] |
117 | if(!graph) |
118 | return pull.error(new Error('unknown graph:' + opts.graph)) |
119 | var cancel, ps = pushable(function () { |
120 | cancel && cancel() |
121 | }) |
122 | |
123 | function push (to, hops) { |
124 | return ps.push(meta ? {id: to, hops: hops} : to) |
125 | } |
126 | |
127 | //by default, also emit your own key. |
128 | if(opts.self !== false) |
129 | push(start, 0) |
130 | |
131 | var conf = config.friends || {} |
132 | cancel = graph.traverse({ |
133 | start: start, |
134 | hops: opts.hops || conf.hops || 3, |
135 | max: opts.dunbar || conf.dunbar || 150, |
136 | each: function (_, to, hops) { |
137 | if(to !== start) push(to, hops) |
138 | } |
139 | }) |
140 | |
141 | if(!live) { cancel(); ps.end() } |
142 | |
143 | return ps |
144 | }, 'createFriendStreamOpts?'), |
145 | |
146 | hops: valid.async(function (start, graph, opts, cb) { |
147 | if (typeof opts == 'function') { // (start|opts, graph, cb) |
148 | cb = opts |
149 | opts = null |
150 | } else if (typeof graph == 'function') { // (start|opts, cb) |
151 | cb = graph |
152 | opts = graph = null |
153 | } |
154 | opts = opts || {} |
155 | if(isString(start)) { // (start, ...) |
156 | // first arg is id string |
157 | opts.start = start |
158 | } else if (start && typeof start == 'object') { // (opts, ...) |
159 | // first arg is opts |
160 | for (var k in start) |
161 | opts[k] = start[k] |
162 | } |
163 | |
164 | var conf = config.friends || {} |
165 | opts.start = opts.start || sbot.id |
166 | opts.dunbar = opts.dunbar || conf.dunbar || 150 |
167 | opts.hops = opts.hops || conf.hops || 3 |
168 | |
169 | var g = graphs[graph || 'follow'] |
170 | if (!g) |
171 | return cb(new Error('Invalid graph type: '+graph)) |
172 | |
173 | awaitSync(function () { |
174 | cb(null, g.traverse(opts)) |
175 | }) |
176 | }, ['feedId', 'string?', 'object?'], ['createFriendStreamOpts']) |
177 | } |
178 | } |
179 |
Built with git-ssb-web