git ssb

2+

mixmix / ticktack



Tree: f3a07a62ed183316f08c7c0cf24dcb767afd29b1

Files: f3a07a62ed183316f08c7c0cf24dcb767afd29b1 / ssb-server-blog-stats.js

5845 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: '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