-
Notifications
You must be signed in to change notification settings - Fork 0
/
anon.coffee
executable file
·156 lines (137 loc) · 4.14 KB
/
anon.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env coffee
ipv6 = require 'ipv6'
async = require 'async'
Twit = require 'twit'
minimist = require 'minimist'
Mustache = require 'mustache'
{WikiChanges} = require 'wikichanges'
argv = minimist process.argv.slice(2), default:
verbose: false
config: './config.json'
address = (ip) ->
if ':' in ip
i = new ipv6.v6.Address(ip)
else
i = new ipv6.v4.Address(ip)
subnetMask = 96 + i.subnetMask
ip = '::ffff:' + i.toV6Group() + "/" + subnetMask
i = new ipv6.v6.Address(ip)
ipToInt = (ip) ->
i = address(ip)
i.bigInteger()
compareIps = (ip1, ip2) ->
r = ipToInt(ip1).compareTo(ipToInt(ip2))
if r == 0
0
else if r > 0
1
else
-1
isIpInRange = (ip, block) ->
if Array.isArray block
compareIps(ip, block[0]) >= 0 and compareIps(ip, block[1]) <= 0
else
a = address(ip)
b = address(block)
a.isInSubnet(b)
isIpInAnyRange = (ip, blocks) ->
for block in blocks
if isIpInRange(ip, block)
return true
return false
getConfig = (path) ->
config = loadJson path
# see if ranges are externally referenced as a separate .json files
if config.accounts
for account in config.accounts
if typeof account.ranges == 'string'
account.ranges = loadJson account.ranges
console.log "loaded config from", path
config
loadJson = (path) ->
if path[0] != '/' and path[0..1] != './'
path = './' + path
require path
getStatusLength = (edit, name, template) ->
# https://support.twitter.com/articles/78124-posting-links-in-a-tweet
fakeUrl = 'http://t.co/BzHLWr31Ce'
status = Mustache.render template, name: name, url: fakeUrl, page: edit.page
status.length
getStatus = (edit, name, template) ->
len = getStatusLength edit, name, template
if len > 140
newLength = edit.page.length - (len - 139)
page = edit.page[0..newLength]
else
page = edit.page
Mustache.render template,
name: name
url: edit.url
page: page
lastChange = {}
isRepeat = (edit) ->
k = "#{edit.wikipedia}"
v = "#{edit.page}:#{edit.user}"
r = lastChange[k] == v
lastChange[k] = v
return r
tweet = (account, status, edit) ->
console.log status
unless argv.noop or (account.throttle and isRepeat(edit))
twitter = new Twit account
twitter.post 'statuses/update', status: status, (err) ->
console.log err if err
inspect = (account, edit) ->
if edit.url
if argv.verbose
console.log edit.url
if account.whitelist and account.whitelist[edit.wikipedia] \
and account.whitelist[edit.wikipedia][edit.page]
status = getStatus edit, edit.user, account.template
tweet account, status, edit
else if account.namespaces? and \
(edit.namespace not in account.namespaces) then
else if account.ranges and edit.anonymous
for name, ranges of account.ranges
if isIpInAnyRange edit.user, ranges
status = getStatus edit, name, account.template
tweet account, status, edit
checkConfig = (config, error) ->
if config.accounts
async.each config.accounts, canTweet, error
else
error "missing accounts stanza in config"
canTweet = (account, error) ->
try
twitter = new Twit account
a = account['access_token']
twitter.get 'search/tweets', q: 'cats', (err, data, response) ->
if err
error err + " for access_token " + a
else if not response.headers['x-access-level'] or \
response.headers['x-access-level'].substring(0,10) != 'read-write'
error "no read-write permission for access token " + a
else
error null
catch err
error "unable to create twitter client for account: " + account
main = ->
config = getConfig argv.config
checkConfig config, (err) ->
if not err
wikipedia = new WikiChanges ircNickname: config.nick
wikipedia.listen (edit) ->
for account in config.accounts
inspect account, edit
else
console.log err
if require.main == module
main()
# for testing
exports.address = address
exports.compareIps = compareIps
exports.isIpInRange = isIpInRange
exports.isIpInAnyRange = isIpInAnyRange
exports.ipToInt = ipToInt
exports.getStatus = getStatus
exports.main = main