git ssb

2+

mixmix / ticktack



Tree: b594e41bf900f3f0b270f809fae0596be6cceb54

Files: b594e41bf900f3f0b270f809fae0596be6cceb54 / ssb-server-ticktack.js

5969 bytesRaw
1const FlumeView = require('flumeview-level')
2const get = require('lodash/get')
3const pull = require('pull-stream')
4const defer = require('pull-defer')
5const isBlog = require('scuttle-blog/isBlog')
6const { isMsg: isMsgRef } = require('ssb-ref')
7
8const getType = (msg) => get(msg, 'value.content.type')
9const getAuthor = (msg) => get(msg, 'value.author')
10const getCommentRoot = (msg) => get(msg, 'value.content.root')
11const getLikeRoot = (msg) => get(msg, 'value.content.vote.link')
12const getTimestamp = (msg) => get(msg, 'value.timestamp')
13
14const FLUME_VIEW_VERSION = 1
15
16module.exports = {
17 name: 'ticktack',
18 version: 1,
19 manifest: {
20 get: 'async',
21 read: 'source',
22 readBlogs: 'source',
23 getBlogs: 'async',
24 readComments: 'source',
25 readAllComments: 'source',
26 readAllLikes: 'source',
27 readAllShares: 'source',
28 readLikes: 'source'
29 },
30 init: (server, config) => {
31 console.log('> initialising ticktack plugin')
32 const myKey = server.keys.id
33
34 const view = server._flumeUse(
35 'ticktack',
36 FlumeView(FLUME_VIEW_VERSION, map)
37 )
38
39 return {
40 get: view.get,
41 read: view.read,
42 readBlogs,
43 getBlogs,
44 readComments,
45 readAllComments,
46 readAllLikes,
47 readAllShares,
48 readLikes
49 // readShares
50 }
51
52 function map (msg, seq) {
53 var root
54
55 switch (getType(msg)) {
56 case 'blog':
57 if (isBlog(msg) && isMyMsg(msg)) return [['B', msg.key, getTimestamp(msg)]]
58 else return []
59
60 case 'vote':
61 root = getLikeRoot(msg)
62 // TODO figure out how to only store likes I care about
63 if (root) return [['L', root, getTimestamp(msg)]]
64 else return []
65
66 // Note this catches:
67 // - all likes, on all things D:
68 // - likes AND unlikes
69
70 case 'post':
71 root = getCommentRoot(msg)
72 // TODO figure out how to only store comments I care about
73 if (!root && isMyMsg(msg) && isPlog(msg)) return [['B', msg.key, getTimestamp(msg)]]
74 else if (root) return [['C', root, getTimestamp(msg)]]
75 else return []
76
77 // Note this catches:
78 // - all comments, on all things D:
79
80 default:
81 return []
82 }
83 }
84
85 function readBlogs (options = {}) {
86 const query = Object.assign({}, {
87 gte: ['B', null, null],
88 // null is the 'minimum' structure in bytewise ordering
89 lte: ['B', undefined, undefined],
90 reverse: true,
91 values: true,
92 keys: false,
93 seqs: false
94 }, options)
95
96 return view.read(query)
97 }
98
99 function getBlogs (options, cb) {
100 if (typeof options === 'function') {
101 cb = options
102 options = {}
103 }
104
105 pull(
106 readBlogs(options),
107 pull.collect(cb)
108 )
109 }
110
111 function readComments (blog, options = {}) {
112 var key = getBlogKey(blog)
113
114 const query = Object.assign({}, {
115 gt: ['C', key, null],
116 lt: ['C', key, undefined],
117 // undefined is the 'maximum' structure in bytewise ordering https://www.npmjs.com/package/bytewise#order-of-supported-structures
118 reverse: true,
119 values: true,
120 keys: false,
121 seqs: false
122 }, options)
123
124 return view.read(query)
125 }
126
127 function readLikes (blog, options = {}) {
128 var key = getBlogKey(blog)
129
130 const query = Object.assign({}, {
131 gt: ['L', key, null],
132 lt: ['L', key, undefined],
133 reverse: true,
134 values: true,
135 keys: false,
136 seqs: false
137 }, options)
138
139 return view.read(query)
140 }
141
142 function readAllComments (opts = {}) {
143 return readAllSource({
144 type: 'post',
145 makeFilter: blogIds => msg => {
146 if (getAuthor(msg) === myKey) return false // exclude my posts
147 if (getCommentRoot(msg) === undefined) return false // want only 'comments' (reply posts)
148 // NOTE - this one will get nested replies too
149
150 return blogIds.includes(getCommentRoot(msg)) // is about one of my blogs
151 },
152 opts
153 })
154 }
155
156 function readAllLikes (opts = {}) {
157 return readAllSource({
158 type: 'vote',
159 makeFilter: blogIds => msg => {
160 if (getAuthor(msg) === myKey) return false // exclude my likes
161
162 return blogIds.includes(getLikeRoot(msg)) // is about one of my blogs
163 },
164 opts
165 })
166 }
167
168 function readAllShares (opts = {}) {
169 return readAllSource({
170 type: 'share',
171 makeFilter: (blogIds) => msg => {
172 if (getAuthor(msg) === myKey) return false // exclude my shares
173
174 return blogIds.includes(getLikeRoot(msg)) // is about one of my blogs
175 },
176 opts
177 })
178 }
179
180 function readAllSource ({ type, makeFilter, opts = {} }) {
181 var source = defer.source()
182
183 getBlogs({ keys: true, values: false }, (err, data) => {
184 if (err) throw err
185
186 const blogIds = data.map(d => d[1])
187
188 opts.type = type
189 var limit = opts.limit
190 delete opts.limit
191 // have to remove limit from the query otherwise Next stalls out if it doesn't get a new result
192
193 const _source = pull(
194 server.messagesByType(opts),
195 pull.filter(makeFilter(blogIds)),
196 limit ? pull.take(limit) : true
197 )
198
199 source.resolve(_source)
200 })
201
202 return source
203 }
204
205 function isMyMsg (msg) {
206 return getAuthor(msg) === myKey
207 }
208 }
209}
210
211function getBlogKey (blog) {
212 if (isMsgRef(blog)) return blog
213 // else if (isMsgRef(blog.key) && isBlog(blog)) return blog.key
214 else if (isMsgRef(blog.key) && (isBlog(blog) || isPlog(blog))) return blog.key
215}
216
217// a Plog is a Blog shaped Post!
218function isPlog (msg) {
219 // if (get(msg, 'value.content.text', '').length >= 2500) console.log(get(msg, 'value.content.text', '').length)
220 return get(msg, 'value.content.text', '').length >= 2500
221}
222

Built with git-ssb-web