git ssb

0+

Dominic / ssb-irc



Tree: 93f8be5cd174b9ced30164c1efaf91df91e4ac0f

Files: 93f8be5cd174b9ced30164c1efaf91df91e4ac0f / ssb.js

5354 bytesRaw
1var pull = require('pull-stream')
2var ref = require('ssb-ref')
3var hash = require('ssb-keys/util').hash
4var markdown = require('ssb-markdown')
5
6function isString (s) {
7 return 'string' == typeof s
8}
9
10function 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
23function 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
43function 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
56exports.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
77function 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
84function getChannel(state, name) {
85 name = name.replace(/^#/,'')
86 return state.channels[name] === true ? name : state.channels[name]
87}
88
89function 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
94exports.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
116exports.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
134exports.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
145exports.tests = [
146 exports.isChannelPost,
147 exports.isChannelMention,
148 exports.isUserMention,
149 exports.isUserFollow
150]
151
152exports.match = function (msg) {
153 return exports.tests.reduce(function (found, test) {
154 return found.concat(test(state, msg) || [])
155 }, [])
156
157}
158
159exports.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
172exports.link = function (id, config) {
173 return ((config && config.irc && config.irc.domain) || "http://viewer.scuttlebot.io") + '/' + encodeURIComponent(id)
174}
175
176if(!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