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