git ssb

0+

Dominic / ssb-irc



Commit 93f8be5cd174b9ced30164c1efaf91df91e4ac0f

separate irc bits and ssb bits

Dominic Tarr committed on 7/20/2017, 10:13:28 AM
Parent: 9be168f4d3e2fb0555b2ef33446fb4f1fddfdb19

Files changed

irc.jsadded
ssb.jsadded
irc.jsView
@@ -1,0 +1,61 @@
1 +var IRC = module.exports = function (config, cb) {
2 + var IRC = require('node-irc')
3 + var conf = config.irc || {}
4 + var irc = new IRC(
5 + conf.host || 'irc.freenode.net',
6 + conf.port || 6667,
7 + conf.name || 'ssbbot'
8 + )
9 + irc.connect()
10 + irc.on('ready', cb)
11 + return irc
12 +}
13 +
14 +IRC.toChannel = function (channel) {
15 + return channel[0] == '#' ? channel : '#'+channel
16 +}
17 +
18 +//join if not already joined...
19 +IRC.join = function (irc, channel) {
20 + if(!irc.channels) irc.channels = {}
21 + if(!irc.channels[channel]) {
22 + irc.channels[channel] = true
23 + irc.join(IRC.toChannel(channel))
24 + }
25 +}
26 +
27 +IRC.channel = function (irc, channel, message) {
28 + IRC.join(irc, channel)
29 + irc.say(IRC.toChannel(channel), message)
30 +}
31 +
32 +IRC.private = function (irc, nick, message) {
33 + irc.say(nick, message)
34 +}
35 +
36 +
37 +if(!module.parent) {
38 + var ssb = require('./ssb')
39 + var irc = IRC({}, function (err) {
40 + var note = {
41 + author: 'dominic',
42 + target: 'domanic',
43 + text: 'test 1 2 3, @domanic',
44 + id: '%v6y1c1VYXthYbNYh0RqmXfC18HyhHnozDN3ZhrWLThU=.sha256'
45 + }
46 + var config = {}
47 +
48 + function notify (note) {
49 + irc.say(
50 + note.type == 'channel'
51 + ? IRC.toChannel(note.target)
52 + : note.target
53 + ,
54 + ssb.render(note, ssb.link(note.id, config))
55 + )
56 + }
57 +
58 + notify(note)
59 + })
60 +}
61 +
ssb.jsView
@@ -1,0 +1,200 @@
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 +

Built with git-ssb-web