Files: 79475b914bb4109c2c52e9c4fc8569b0e48e87df / ssb.js
5354 bytesRaw
1 | var pull = require('pull-stream') |
2 | var ref = require('ssb-ref') |
3 | var hash = require('ssb-keys/util').hash |
4 | var markdown = require('ssb-markdown') |
5 | |
6 | function isString (s) { |
7 | return 'string' == typeof s |
8 | } |
9 | |
10 | function update (state, msg) { |
11 | if(msg.content.type === 'channel') { |
12 | if(msg.content.subscribed) |
13 | state.channels[msg.content.channel] = msg.content.irc || msg.content.channel |
14 | else |
15 | delete state.channels[msg.content.channel] |
16 | } |
17 | else if(msg.content.type === 'about' && ref.isFeed(msg.content.about) && (msg.content.name || msg.content.irc != null)) { |
18 | state.users[msg.content.about] = msg.content.irc == null ? msg.content.name : msg.content.irc |
19 | } |
20 | return state |
21 | } |
22 | |
23 | function init(sbot, id, cb) { |
24 | var state = {users: {}, channels: {}}, int, called |
25 | |
26 | //hack since createUserStream doesn't supply sync: correctly. |
27 | pull( |
28 | sbot.createUserStream({id: id, limit: 1, reverse: true}), |
29 | pull.collect(function (err, ary) { |
30 | pull( |
31 | sbot.createHistoryStream({id: id || sbot.id, live: true}), |
32 | pull.drain(function (msg) { |
33 | state = update(state, msg.value) |
34 | if(msg.timestamp === ary[0].timestamp) |
35 | cb && cb(null, state) |
36 | }) |
37 | ) |
38 | }) |
39 | ) |
40 | return state |
41 | } |
42 | |
43 | function toKey(fn) { |
44 | return function (state, msg) { |
45 | var key |
46 | if(msg.key) { |
47 | key = msg.key |
48 | msg = msg.value |
49 | } |
50 | else |
51 | key = '%'+hash(JSON.stringify(msg, null, 2)) |
52 | return fn(state, msg, key) |
53 | } |
54 | } |
55 | |
56 | exports.isChannelPost = toKey(function isChannelPost (state, msg, key) { |
57 | if(state.channels[msg.content.channel] && !msg.content.root) { |
58 | var text = msg.content.text |
59 | if(isString(text) && text) { |
60 | text = text.split('\n') |
61 | if(text.length > 1) |
62 | text = text[0] |
63 | else |
64 | text = text[0] |
65 | |
66 | return [{ |
67 | author: msg.author, |
68 | target: msg.content.channel, |
69 | text: text.length > 100 ? text.substring(0, 97)+'...' : text, |
70 | id: key, |
71 | type: 'channel' |
72 | }] |
73 | } |
74 | } |
75 | }) |
76 | |
77 | function uniq (a) { |
78 | var b = [] |
79 | for(var i = 0; i < a.length; i++) |
80 | if(!~b.indexOf(a[i])) b.push(a[i]) |
81 | return b |
82 | } |
83 | |
84 | function getChannel(state, name) { |
85 | name = name.replace(/^#/,'') |
86 | return state.channels[name] === true ? name : state.channels[name] |
87 | } |
88 | |
89 | function surrounding(text, match, length) { |
90 | var i = Math.max(text.indexOf(match)-100, 0) |
91 | return (i > 0 ? '...' : '') + text.substring(i, i+100) + (i + 100 < text.length ? '...' : '') |
92 | } |
93 | |
94 | exports.isChannelMention = toKey(function (state, msg, key) { |
95 | var text = msg.content.text |
96 | if(isString(text) && text) { |
97 | text = markdown.inline(text) |
98 | if(!/(#\w+)/.test(text)) return [] |
99 | |
100 | var m = uniq(text.match(/(#\w+)/g)) |
101 | return m.filter(function (name) { |
102 | return getChannel(state, name) |
103 | }).map(function (name) { |
104 | return { |
105 | author: msg.author, |
106 | target: getChannel(state, name), |
107 | text: surrounding(text, name, 100), |
108 | id: key, |
109 | type: 'channel' |
110 | } |
111 | }) |
112 | } |
113 | return [] |
114 | }) |
115 | |
116 | exports.isUserMention = toKey(function (state, msg, key) { |
117 | var text = msg.content.text |
118 | if(isString(text) && text && Array.isArray(msg.content.mentions)) { |
119 | text = markdown.inline(text) |
120 | return msg.content.mentions.filter(function (mention) { |
121 | return state.users[mention.link] |
122 | }).map(function (mention) { |
123 | return { |
124 | author: msg.author, |
125 | target: state.users[mention.link], |
126 | text: surrounding(markdown.inline(text), '@'+mention.name, 100), |
127 | id: key, |
128 | type: 'user' |
129 | } |
130 | }) |
131 | } |
132 | }) |
133 | |
134 | exports.isUserFollow = toKey(function (state, msg, key) { |
135 | if(msg.content.type == 'contact' && msg.content.following && state.users[msg.content.contact]) |
136 | return { |
137 | author: msg.author, |
138 | target: state.users[msg.content.contact], |
139 | text: 'followed you', //this will make sense since the notification will be a direct message |
140 | id: key, |
141 | type: 'user' |
142 | } |
143 | }) |
144 | |
145 | exports.tests = [ |
146 | exports.isChannelPost, |
147 | exports.isChannelMention, |
148 | exports.isUserMention, |
149 | exports.isUserFollow |
150 | ] |
151 | |
152 | exports.match = function (msg) { |
153 | return exports.tests.reduce(function (found, test) { |
154 | return found.concat(test(state, msg) || []) |
155 | }, []) |
156 | |
157 | } |
158 | |
159 | exports.render = function (note, link) { |
160 | //personal mentions are just cluttered with the target and action |
161 | //since it's only shown to one person it's implied. |
162 | if(note.target[0] !== '#') |
163 | return note.author + ':' + note.text + (link ? (' ' + link) : '') |
164 | |
165 | return ( |
166 | [note.author, note.action, note.target].join(' ') + ': ' + |
167 | note.text + ' ' + |
168 | (link || '') |
169 | ) |
170 | } |
171 | |
172 | exports.link = function (id, config) { |
173 | return ((config && config.irc && config.irc.domain) || "http://viewer.scuttlebot.io") + '/' + encodeURIComponent(id) |
174 | } |
175 | |
176 | if(!module.parent) { |
177 | //this is just for testing... |
178 | require('ssb-client')(function (err, sbot) { |
179 | if(err) throw err |
180 | var state = init(sbot, process.argv[2] || sbot.id, function (err, state) { |
181 | |
182 | console.log(state) |
183 | //XXX: properly persist state with a flumeview? |
184 | |
185 | pull( |
186 | sbot.createLogStream({}), |
187 | pull.drain(function (msg) { |
188 | if(msg.sync) return |
189 | var a = tests.reduce(function (found, test) { |
190 | return found.concat(exports.match(state, msg) || []) |
191 | }, []) |
192 | if(a.length) { |
193 | |
194 | } |
195 | }) |
196 | ) |
197 | }) |
198 | }) |
199 | } |
200 | |
201 |
Built with git-ssb-web