git ssb

16+

cel / patchfoo



Tree: d185716fb11842271c37ff499725b2b66ba2a5e3

Files: d185716fb11842271c37ff499725b2b66ba2a5e3 / lib / contacts.js

8721 bytesRaw
1var pull = require('pull-stream')
2var defer = require('pull-defer')
3var many = require('pull-many')
4var util = require('./util')
5
6module.exports = Contacts
7
8function Contacts(sbot) {
9 if (!(this instanceof Contacts)) return new Contacts(sbot)
10 this.sbot = sbot
11 if (!sbot.links) {
12 // No ssb-links: need to use ssb-friends singleton
13 if (sbot.friends && typeof sbot.friends.graphStream === 'function') {
14 this._startGraphStream();
15 } else {
16 console.error('Missing ssb-friends or sbot.links')
17 }
18 }
19}
20
21Contacts.prototype._startGraphStream = function () {
22 const graph = this.graph = {}
23 let waiting = this._waiting = [];
24 const self = this;
25 pull(
26 this.sbot.friends.graphStream({
27 old: true,
28 live: true,
29 }),
30 pull.drain(function (obj) {
31 for (const fromId in obj) {
32 const map = obj[fromId]
33 const map2 = graph[fromId] || (graph[fromId] = {})
34 for (const toId in map) {
35 map2[toId] = map[toId]
36 }
37 }
38 if (waiting) {
39 delete self._waiting;
40 delete waiting
41 while (waiting.length) {
42 waiting.shift()();
43 }
44 }
45 }, err => {
46 if (waiting) {
47 delete self._waiting;
48 delete waiting
49 while (waiting.length) {
50 waiting.shift()(err)
51 }
52 }
53 console.trace(err)
54 })
55 )
56}
57
58Contacts.prototype._createContactStream = function (source, dest) {
59 if (this.sbot.links) return this._createContactStreamLinks(source, dest)
60 if (this.graph) return this._createContactStreamGraph(source, dest)
61 return pull.error(new Error('missing sbot.links or ssb-friends'))
62}
63
64Contacts.prototype._createContactStreamLinks = function (source, dest) {
65 return pull(
66 this.sbot.links({
67 source: source,
68 dest: dest,
69 rel: 'contact',
70 values: true,
71 reverse: true
72 }),
73 pull.filter(function (msg) {
74 var c = msg && msg.value && msg.value.content
75 return c && c.type === 'contact' && (!dest || c.contact === dest)
76 }),
77 pull.map(function (msg) {
78 var c = msg && msg.value && msg.value.content
79 return {
80 source: msg.value.author,
81 dest: c.contact,
82 msg: msg,
83 value: c.following ? true : c.flagged || c.blocking ? false : null
84 }
85 }),
86 pull.unique(function (edge) {
87 return edge.source + '-' + edge.dest
88 })
89 )
90}
91
92Contacts.prototype._createContactStreamGraph = function (source, dest) {
93 if (this._waiting) {
94 return u.readNext(cb => {
95 this._waiting.push(err => {
96 if (err) return cb(err)
97 cb(this._createContactStreamGraph(source, dest))
98 })
99 })
100 }
101
102 let edges = []
103 function mapFrom(source, dest, value) {
104 return {
105 source,
106 dest,
107 value: value >= 1 ? true : value == -1 ? false : null
108 // Note: cannot add msg here, as it's not provided by ssb-friends
109 }
110 }
111 if (source) {
112 const from = this.graph[source]
113 if (dest == null) {
114 edges.push(mapFrom(source, dest, from[dest]))
115 } else for (const d in from) {
116 edges.push(mapFrom(source, d, from[d]))
117 }
118 } else if (dest) {
119 for (const source in this.graph) {
120 const from = this.graph[source]
121 edges.push(mapFrom(source, dest, from[dest]))
122 }
123 } else {
124 return pull.error(new TypeError('source or dest required'))
125 }
126 return pull.values(edges)
127}
128
129Contacts.prototype.createFollowsStream = function (id) {
130 return pull(
131 this._createContactStream(id, null),
132 pull.filter('value'),
133 pull.map('dest')
134 )
135}
136
137Contacts.prototype.createFollowersStream = function (id) {
138 return pull(
139 this._createContactStream(null, id),
140 pull.filter('value'),
141 pull.map('source')
142 )
143}
144
145Contacts.prototype.createFollowedFollowersStream = function (source, dest) {
146 var follows = {}, followers = {}
147 return pull(
148 many([
149 this._createContactStream(source, null),
150 this._createContactStream(null, dest)
151 ]),
152 pull.filter('value'),
153 pull.map(function (edge) {
154 if (edge.source === source) {
155 if (followers[edge.dest]) {
156 delete followers[edge.dest]
157 return edge.dest
158 } else {
159 follows[edge.dest] = true
160 }
161 } else if (edge.dest === dest) {
162 if (follows[edge.source]) {
163 delete follows[edge.source]
164 return edge.source
165 } else {
166 followers[edge.source] = true
167 }
168 }
169 }),
170 pull.filter()
171 )
172}
173
174Contacts.prototype.createFriendsStream = function (opts, endCb) {
175 if (typeof opts === 'string') opts = {id: opts}
176 var id = opts.id
177 var msgIds = opts.msgIds
178 var follows = {}, followers = {}
179 var blocks = {}, blockers = {}
180 var enemies = opts.enemies && {}
181 return pull(
182 many([
183 this._createContactStream(id, null),
184 this._createContactStream(null, id)
185 ]),
186 pull.map(function (edge) {
187 if (edge.value) {
188 if (edge.source === id) {
189 if (followers[edge.dest]) {
190 var item2 = followers[edge.dest]
191 delete followers[edge.dest]
192 return msgIds ? {feed: edge.dest, msg: edge.msg, msg2: item2.msg} : edge.dest
193 } else {
194 follows[edge.dest] = msgIds ? {feed: edge.dest, msg: edge.msg} : edge.dest
195 }
196 } else if (edge.dest === id) {
197 if (follows[edge.source]) {
198 var item2 = follows[edge.source]
199 delete follows[edge.source]
200 return msgIds ? {feed: edge.source, msg: edge.msg, msg2: item2.msg} : edge.source
201 } else {
202 followers[edge.source] = msgIds ? {feed: edge.source, msg: edge.msg} : edge.source
203 }
204 }
205 } else if (edge.value === false) {
206 if (edge.source === id) {
207 if (enemies && blockers[edge.dest]) {
208 var item2 = blockers[edge.dest]
209 delete blockers[edge.dest]
210 enemies[edge.dest] = msgIds ? {feed: edge.dest, msg: edge.msg, msg2: item2.msg} : edge.dest
211 } else {
212 blocks[edge.dest] = msgIds ? {feed: edge.dest, msg: edge.msg} : edge.dest
213 }
214 } else if (edge.dest === id) {
215 if (enemies && blocks[edge.source]) {
216 var item2 = blocks[edge.source]
217 delete blocks[edge.source]
218 enemies[edge.source] = msgIds ? {feed: edge.source, msg: edge.msg, msg2: item2.msg} : edge.source
219 } else {
220 blockers[edge.source] = msgIds ? {feed: edge.source, msg: edge.msg} : edge.source
221 }
222 }
223 }
224 }),
225 pull.filter(),
226 endCb && function (read) {
227 return function (abort, cb) {
228 read(abort, function (end, data) {
229 cb(end, data)
230 if (end) endCb(end === true ? null : end, {
231 followers: Object.values(followers),
232 follows: Object.values(follows),
233 blocks: Object.values(blocks),
234 blockers: Object.values(blockers),
235 enemies: Object.values(enemies),
236 })
237 })
238 }
239 }
240 )
241}
242
243Contacts.prototype.createContactStreams = function (opts) {
244 var msgIds = opts.msgIds
245 var follows = defer.source()
246 var followers = defer.source()
247 var blocks = defer.source()
248 var blockers = defer.source()
249 var enemies = defer.source()
250 var friends = this.createFriendsStream(opts, function (err, more) {
251 try {
252 follows.resolve(err ? pull.error(err) : pull.values(more.follows))
253 followers.resolve(err ? pull.error(err) : pull.values(more.followers))
254 blocks.resolve(err ? pull.error(err) : pull.values(more.blocks))
255 blockers.resolve(err ? pull.error(err) : pull.values(more.blockers))
256 enemies.resolve(err ? pull.error(err) : pull.values(more.enemies))
257 } catch(e) {
258 console.trace(e)
259 }
260 })
261 return {
262 friends: friends,
263 follows: follows,
264 followers: followers,
265 enemies: enemies,
266 blocks: blocks,
267 blockers: blockers,
268 }
269}
270
271Contacts.prototype.get = function (source, dest, cb) {
272 if (this.sbot.links) return pull(
273 this.sbot.links({source, dest, rel: 'contact', reverse: true,
274 values: true, meta: false, keys: false}),
275 pull.filter(value => {
276 const c = value && !value.private && value.content
277 return c && c.type === 'contact'
278 }),
279 pull.take(1),
280 pull.reduce((acc, value) => {
281 // trinary logic from ssb-friends
282 const c = value && value.content
283 return c.following ? true : c.flagged || c.blocking ? false : null
284 }, null, cb)
285 )
286
287 if (this._waiting) {
288 return this._waiting.push(err => {
289 if (err) return cb(err)
290 this.get(source, dest, cb)
291 })
292 }
293
294 const map = this.graph[source]
295 const value = map && map[dest]
296 cb(null, value >= 1 ? true : value == -1 ? false : null)
297}
298

Built with git-ssb-web