wcc 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|