wcc 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -2
- data/doc/Filters.md +5 -5
- data/lib/wcc/mail.rb +2 -1
- data/lib/wcc/site.rb +18 -6
- data/lib/wcc/syslog.rb +22 -2
- data/lib/wcc/version.rb +1 -1
- data/lib/wcc.rb +58 -16
- metadata +4 -4
data/README.md
CHANGED
@@ -2,11 +2,11 @@ web change checker
|
|
2
2
|
==================
|
3
3
|
|
4
4
|
This is a powerful ruby program to track changes of websites and get notified via mail on
|
5
|
-
change with configurable scope of
|
5
|
+
change with configurable scope of addresses per website. All mails contain a unified diff
|
6
6
|
from old content to new content so minor changes produce only few lines of text even on large sites.
|
7
7
|
|
8
8
|
Since version 2.0 wcc has a completely rewritten notification system so emails are now only *one*
|
9
|
-
way to
|
9
|
+
way to receive notifications - the currently supported other are XMPP/Jabber and the syslog.
|
10
10
|
These changes are reflected in 'conf.yml' as well so take care of migrating it (that basically
|
11
11
|
means to create a *recipients* section and update your site entries from *emails* to *notify*).
|
12
12
|
|
data/doc/Filters.md
CHANGED
@@ -8,23 +8,23 @@ to notify the user (by email) or not.
|
|
8
8
|
These Proc blocks get registered using `Filter.add` with a given ID (a
|
9
9
|
string, not a :symbol to prevent some problems).
|
10
10
|
|
11
|
-
wcc provides an
|
11
|
+
wcc provides an auto-loading mechanism that loads all ruby files contained in
|
12
12
|
a specific directory - the *filter.d* - via `require`. The filter file
|
13
13
|
should contain some bootstrap code that may rely only on the *Filter* class
|
14
14
|
out of wcc:
|
15
15
|
|
16
16
|
Filter.add 'my_custom_id' do |data,arguments|
|
17
17
|
# do your filter magic here
|
18
|
-
# and return a
|
18
|
+
# and return a Boolean
|
19
19
|
end
|
20
20
|
|
21
21
|
The format of the `data` may change over time, currently it's a string
|
22
|
-
containing all lines separated by newline of the unified diff (raw from
|
22
|
+
containing all lines separated by newline of the unified diff (raw from Unix
|
23
23
|
diff). The arguments are a - possibly empty - hash directly passed in from
|
24
|
-
the configuration file to
|
24
|
+
the configuration file to parametrize the filter (e.g. ignoring all diffs
|
25
25
|
shorter than let's say 10 lines).
|
26
26
|
|
27
|
-
The output of this filter should be
|
27
|
+
The output of this filter should be Boolean true or false, indicating that
|
28
28
|
an email should be sent or not.
|
29
29
|
|
30
30
|
In the configuration file a filter is referenced like this:
|
data/lib/wcc/mail.rb
CHANGED
@@ -84,9 +84,10 @@ module WCC
|
|
84
84
|
:smtp_port => conf['smtp']['port'] || 25
|
85
85
|
}
|
86
86
|
elsif conf['fake_file'].is_a?(Hash)
|
87
|
+
from_mail = MailAddress.new(conf['fake_file']['from'] || "#{Etc.getlogin}@localhost")
|
87
88
|
return {
|
88
89
|
:mailer => 'fake_file',
|
89
|
-
:from_mail =>
|
90
|
+
:from_mail => from_mail
|
90
91
|
}
|
91
92
|
end
|
92
93
|
end
|
data/lib/wcc/site.rb
CHANGED
@@ -55,13 +55,25 @@ module WCC
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def fetch
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
retrieve(@uri)
|
59
|
+
end
|
60
|
+
|
61
|
+
def fetch_redirect(new_uri)
|
62
|
+
retrieve(new_uri)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def retrieve(uri)
|
68
|
+
con = Net::HTTP.new(uri.host, uri.port)
|
69
|
+
if uri.is_a?(URI::HTTPS)
|
70
|
+
con.use_ssl = true
|
71
|
+
con.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
62
72
|
end
|
63
|
-
|
64
|
-
|
73
|
+
con.start do |http|
|
74
|
+
#http.open_timeout = 20
|
75
|
+
#http.read_timeout = 60
|
76
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
65
77
|
if @auth['type'] == 'basic'
|
66
78
|
WCC.logger.debug "Doing basic auth"
|
67
79
|
req.basic_auth(@auth['username'], @auth['password'])
|
data/lib/wcc/syslog.rb
CHANGED
@@ -1,11 +1,31 @@
|
|
1
1
|
|
2
2
|
module WCC
|
3
3
|
class SyslogNotificator
|
4
|
-
|
4
|
+
|
5
|
+
LEVELS = ['crit', 'emerg', 'alert', 'err', 'warning', 'notice', 'info', 'debug']
|
6
|
+
|
7
|
+
def initialize(opts)
|
8
|
+
if LEVELS.include?(opts)
|
9
|
+
@prio = opts
|
10
|
+
@enable = true
|
11
|
+
else
|
12
|
+
@enable = false
|
13
|
+
raise ArgumentError, "The given priority '#{opts}' is not known, use one of: #{LEVELS.join(', ')}."
|
14
|
+
end
|
15
|
+
begin
|
16
|
+
# from ruby std lib
|
17
|
+
require 'syslog'
|
18
|
+
rescue LoadError
|
19
|
+
@enable = false
|
20
|
+
raise ArgumentError, "Won't log to syslog since your system does NOT support syslog!"
|
21
|
+
end
|
5
22
|
end
|
6
23
|
|
24
|
+
# TODO: ERB template for syslog
|
7
25
|
def notify!(data)
|
8
|
-
|
26
|
+
Syslog.open(data.tag, Syslog::LOG_PID | Syslog::LOG_CONS) do |s|
|
27
|
+
s.send(@prio.to_sym, "Change at #{data.site.uri.to_s} (tag #{data.site.id}) detected")
|
28
|
+
end if @enable
|
9
29
|
end
|
10
30
|
|
11
31
|
def self.parse_conf(conf); {} end
|
data/lib/wcc/version.rb
CHANGED
data/lib/wcc.rb
CHANGED
@@ -104,7 +104,7 @@ module WCC
|
|
104
104
|
:cache_dir => '/var/tmp/wcc',
|
105
105
|
:tag => 'wcc',
|
106
106
|
:filter_dir => './filter.d',
|
107
|
-
:template_dir => './template.d'
|
107
|
+
:template_dir => './template.d'
|
108
108
|
}
|
109
109
|
end
|
110
110
|
|
@@ -211,20 +211,9 @@ module WCC
|
|
211
211
|
yaml_rec[name].to_a.each do |yaml_way|
|
212
212
|
# TODO: find options and pass them to every notificator
|
213
213
|
if yaml_way.is_a?(Hash)
|
214
|
-
|
215
|
-
klass = Notificators.mappings[prim_key]
|
216
|
-
if klass.nil?
|
217
|
-
WCC.logger.error "Referenced notificator '#{prim_key}' not found!"
|
218
|
-
else
|
219
|
-
rec << klass.new(yaml_way[prim_key])
|
220
|
-
end
|
214
|
+
new_notificator(name, yaml_way.keys.first, yaml_way[yaml_way.keys.first], rec)
|
221
215
|
else
|
222
|
-
|
223
|
-
if klass.nil?
|
224
|
-
WCC.logger.error "Referenced notificator '#{yaml_way}' not found!"
|
225
|
-
else
|
226
|
-
rec << klass.new
|
227
|
-
end
|
216
|
+
new_notificator(name, yaml_way, nil, rec)
|
228
217
|
end
|
229
218
|
end
|
230
219
|
@recipients[name] = rec
|
@@ -289,9 +278,26 @@ module WCC
|
|
289
278
|
return Conf.instance.recipients
|
290
279
|
end
|
291
280
|
|
292
|
-
def self.file(path = nil) File.join(self[:cache_dir], path) end
|
281
|
+
def self.file(path = nil); File.join(self[:cache_dir], path) end
|
293
282
|
def self.simulate?; self[:simulate] end
|
294
283
|
def self.[](key); Conf.instance[key] end
|
284
|
+
|
285
|
+
private
|
286
|
+
|
287
|
+
def new_notificator(rec_name, not_name, opts, rec_out)
|
288
|
+
klass = Notificators.mappings[not_name]
|
289
|
+
if klass.nil?
|
290
|
+
WCC.logger.error "Referenced notificator '#{not_name}' not found!"
|
291
|
+
return
|
292
|
+
end
|
293
|
+
begin
|
294
|
+
if opts.nil?; rec_out << klass.new
|
295
|
+
else rec_out << klass.new(opts)
|
296
|
+
end
|
297
|
+
rescue => ex
|
298
|
+
WCC.logger.error "Recipient '#{rec_name}' not notifiable via '#{not_name}' (skip): #{ex}"
|
299
|
+
end
|
300
|
+
end
|
295
301
|
end
|
296
302
|
|
297
303
|
class Notificators
|
@@ -346,8 +352,44 @@ module WCC
|
|
346
352
|
WCC.logger.error "Cannot connect to #{site.uri.to_s} : #{ex.to_s}"
|
347
353
|
return false
|
348
354
|
end
|
349
|
-
if
|
355
|
+
if res.kind_of?(Net::HTTPOK)
|
356
|
+
# be happy!
|
357
|
+
elsif res.kind_of?(Net::HTTPMovedPermanently)
|
358
|
+
loc = res['Location']
|
359
|
+
if loc.nil?
|
360
|
+
WCC.logger.error "Site #{site.uri.to_s} moved permanently, skippong it - no new location given."
|
361
|
+
else
|
362
|
+
WCC.logger.error "Site #{site.uri.to_s} moved permanently to '#{loc}', skipping it - please update your conf.yml adequately!"
|
363
|
+
end
|
364
|
+
return false
|
365
|
+
elsif res.kind_of?(Net::HTTPSeeOther) or res.kind_of?(Net::HTTPTemporaryRedirect)
|
366
|
+
loc = URI.parse(res['Location'])
|
367
|
+
WCC.logger.warn "Redirect: requesting '#{loc.to_s}'"
|
368
|
+
res = site.fetch_redirect(loc)
|
369
|
+
if not res.kind_of?(Net::HTTPOK)
|
370
|
+
WCC.logger.error "Redirected site #{loc.to_s} returned #{res.code} code, skipping it."
|
371
|
+
WCC.logger.error "Headers: #{res.to_hash.inspect}"
|
372
|
+
return false
|
373
|
+
end
|
374
|
+
elsif res.kind_of?(Net::HTTPUnauthorized)
|
375
|
+
WCC.logger.error "Site #{site.uri.to_s} demands authentication for '#{res['www-authenticate']}', skipping it - consider using 'auth:' option in your conf.yml."
|
376
|
+
return false
|
377
|
+
elsif res.kind_of?(Net::HTTPNotFound)
|
378
|
+
WCC.logger.error "Site #{site.uri.to_s} not found, skipping it."
|
379
|
+
return false
|
380
|
+
elsif res.kind_of?(Net::HTTPForbidden)
|
381
|
+
WCC.logger.error "Site #{site.uri.to_s} forbids access, skipping it."
|
382
|
+
return false
|
383
|
+
elsif res.kind_of?(Net::HTTPInternalServerError)
|
384
|
+
WCC.logger.error "Site #{site.uri.to_s} has internal errors, skipping it."
|
385
|
+
return false
|
386
|
+
elsif res.kind_of?(Net::HTTPServiceUnavailable)
|
387
|
+
#retry_after = res['Retry-After']
|
388
|
+
WCC.logger.warn "Site #{site.uri.to_s} currently not available, skipping it."
|
389
|
+
return false
|
390
|
+
else
|
350
391
|
WCC.logger.error "Site #{site.uri.to_s} returned #{res.code} code, skipping it."
|
392
|
+
WCC.logger.error "Headers: #{res.to_hash.inspect}"
|
351
393
|
return false
|
352
394
|
end
|
353
395
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wcc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 2.
|
10
|
+
version: 2.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Christian Nicolai
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-11-
|
18
|
+
date: 2011-11-24 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: htmlentities
|