wcc 1.3.0 → 2.0.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 +19 -9
- data/assets/conf.yml +43 -13
- data/assets/template.d/mail.alt.erb +2 -2
- data/assets/template.d/mail.plain.erb +2 -2
- data/assets/template.d/xmpp-body.plain.erb +9 -0
- data/lib/wcc.rb +54 -53
- data/lib/wcc/mail.rb +95 -56
- data/lib/wcc/site.rb +3 -3
- data/lib/wcc/syslog.rb +15 -0
- data/lib/wcc/version.rb +4 -0
- data/lib/wcc/xmpp.rb +69 -0
- metadata +31 -11
data/README.md
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
web change checker
|
2
2
|
==================
|
3
3
|
|
4
|
-
This is a
|
4
|
+
This is a powerful ruby program to track changes of websites and get notified via mail on
|
5
5
|
change with configurable scope of adresses 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
|
-
|
9
|
-
|
8
|
+
Since version 2.0 wcc has a completely rewritten notification system so emails are now only *one*
|
9
|
+
way to recieve notifications - the currently supported other are XMPP/Jabber and the Syslog.
|
10
|
+
These changes are reflected in 'conf.yml' as well so take care of migrating it (that basically
|
11
|
+
means to create a *recipients* section and update your site entries from *emails* to *notify*).
|
12
|
+
|
13
|
+
Note: wcc relies on native `diff` command to produce the unified diff shown in mails and native
|
14
|
+
`syslog` command as well as user information from /etc/login.
|
10
15
|
|
11
16
|
Setup
|
12
17
|
-----
|
13
18
|
|
14
|
-
You need Ruby (preferably version 1.8.7) and Rubygems installed
|
19
|
+
You need Ruby (preferably version 1.8.7, Ruby 1.9 untested) and Rubygems installed
|
15
20
|
(consider using [rvm](http://beginrescueend.com/)). Install wcc:
|
16
21
|
|
17
22
|
gem install wcc
|
@@ -46,13 +51,18 @@ can itself be given on command line as last argument. Each option has a hard-cod
|
|
46
51
|
(e.g. the configuration file name is assumed to be './conf.yml'). Command line options
|
47
52
|
overwrite configuration file entries.
|
48
53
|
|
49
|
-
|
50
|
-
|
51
|
-
|
54
|
+
To see how such a configuration might look open 'conf.yml' in your '/my/conf' directory after
|
55
|
+
doing `wcc-init` - it contains a bunch of comments that describe your options. The basic structure
|
56
|
+
is made up from three sections: generic entries in *conf*, user profiles in *recipients* and a
|
57
|
+
list of sites to check for changes in *sites*. Each site entry should contain an URL and a
|
58
|
+
list of user profile names to notify on change.
|
59
|
+
|
60
|
+
An example crontab entry that runs wcc every 5 minutes might look like this:
|
52
61
|
|
53
|
-
|
62
|
+
*/5 * * * * root cd /my/conf;./wcc
|
54
63
|
|
55
|
-
|
64
|
+
Since you can configure an individual check_interval per site these 5 minutes in crontab are only
|
65
|
+
the least common multiple for wcc check if each sites check_interval has passed.
|
56
66
|
|
57
67
|
By default wcc only outputs ERROR and FATAL messages to avoid your cron daemon spammin' around.
|
58
68
|
It is recommended to place 'conf.yml' (and optionally the 'filter.d' and 'template.d') within
|
data/assets/conf.yml
CHANGED
@@ -1,43 +1,73 @@
|
|
1
1
|
# web-change-checker
|
2
2
|
|
3
3
|
conf:
|
4
|
-
from_addr: root@localhost
|
5
4
|
# # /var/tmp won't get ereased on restart!
|
6
5
|
# cache_dir: /var/tmp/wcc
|
7
6
|
# tag: wcc
|
8
|
-
# use_syslog: yes
|
9
7
|
# filterd: ./filter.d
|
10
8
|
# templated: ./template.d
|
11
9
|
# email:
|
12
10
|
# smtp:
|
11
|
+
# from: root@localhost
|
13
12
|
# host: localhost
|
14
13
|
# port: 25
|
15
|
-
# ...or for TESTING purposes:
|
14
|
+
# # ... or for TESTING purposes only:
|
16
15
|
# email:
|
17
|
-
# fake_file
|
16
|
+
# fake_file:
|
17
|
+
# from: root@localhost
|
18
|
+
# # this allows wcc to send IM messages via
|
19
|
+
# # it's own jabber account
|
20
|
+
# jabber:
|
21
|
+
# jid: wcc@example.org/wcc
|
22
|
+
# password: s3cr3t
|
23
|
+
|
24
|
+
# a recipient is like a user profile, it says in which ways wcc
|
25
|
+
# might contact you and provides a central place where to change
|
26
|
+
# email addresses etc.
|
27
|
+
recipients:
|
28
|
+
- me:
|
29
|
+
- email: me@my.place
|
30
|
+
- jabber: me@jabber.org
|
31
|
+
- my_friend:
|
32
|
+
- email: mail@example.com
|
33
|
+
# 'syslog' profile that notifies syslog on change
|
34
|
+
- syslog:
|
35
|
+
- syslog
|
18
36
|
|
19
37
|
sites:
|
20
38
|
- url: http://google.com/
|
21
|
-
|
22
|
-
-
|
23
|
-
-
|
39
|
+
notify:
|
40
|
+
- me
|
41
|
+
- my_friend
|
42
|
+
- syslog
|
43
|
+
|
44
|
+
# Filters
|
45
|
+
- url: http:/filter-test.com/
|
46
|
+
#notify: ...
|
47
|
+
# These filters will be executed and every single one has to
|
48
|
+
# return 'true' for the user to be notified
|
24
49
|
filters:
|
25
50
|
- test
|
26
51
|
- arg-test: {number: 5, hello: world}
|
27
52
|
- only_changes_of: {at_least: 4, t: lines}
|
28
53
|
# Regex filter that performs matching against <scope> (one of full or diff)
|
29
54
|
- matches: {regex: '(normal|regex)[!]+', flags: i, scope: diff}
|
30
|
-
|
55
|
+
|
56
|
+
# HTTP Basic Auth
|
31
57
|
- url: https://my.secret.place/
|
32
|
-
|
33
|
-
|
34
|
-
# only supports basic auth currently
|
58
|
+
#notify: ...
|
59
|
+
# Only supports basic auth currently
|
35
60
|
auth: {type: basic, username: me, password: secret}
|
61
|
+
|
62
|
+
# (Session) Cookie
|
36
63
|
- url: http://your.cms.com/
|
37
|
-
#
|
64
|
+
#notify: ...
|
38
65
|
# Don't add trailing newline just the pure bytes of your cookie
|
39
66
|
cookie: file.cookie
|
67
|
+
|
68
|
+
# Check interval
|
40
69
|
- url: http://heavily.loaded.site/
|
41
|
-
#
|
70
|
+
#notify: ...
|
71
|
+
# This will check at most every 30 minutes
|
42
72
|
# (or next time ´wcc´ gets called after 30mins passed)
|
43
73
|
check_interval: 30
|
@@ -1,5 +1,5 @@
|
|
1
|
-
From: <%= from.name %> <<%= from.address %>>
|
2
|
-
To: <%= to %>
|
1
|
+
From: <%= data.from.name %> <<%= data.from.address %>>
|
2
|
+
To: <%= data.to %>
|
3
3
|
Subject: [<%= data.tag %>] <%= data.site.uri.host %> changed
|
4
4
|
Content-Type: multipart/alternative; boundary="<%= data.boundary %>"
|
5
5
|
MIME-Version: 1.0
|
@@ -1,5 +1,5 @@
|
|
1
|
-
From: <%= from.name %> <<%= from.address %>>
|
2
|
-
To: <%= to %>
|
1
|
+
From: <%= data.from.name %> <<%= data.from.address %>>
|
2
|
+
To: <%= data.to %>
|
3
3
|
Subject: [<%= data.tag %>] <%= data.site.uri.host %> changed
|
4
4
|
Content-Type: text/plain; charset="utf-8"
|
5
5
|
Content-Transfer-Encoding: base64
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<% if data.diff.nil? %>
|
2
|
+
Checked <%= data.site.uri.to_s %> the first time so no diff was possible.
|
3
|
+
<% else %>
|
4
|
+
Change at <%= data.site.uri.to_s %> - diff follows:
|
5
|
+
|
6
|
+
<%= data.diff.to_s %>
|
7
|
+
|
8
|
+
nHunks: <%= data.diff.nhunks %>, nIns: <%= data.diff.ninsertions %>, nDel: <%= data.diff.ndeletions %>, nLinesC: <%= data.diff.nlinesc %>, nCharsC: <%= data.diff.ncharsc %>
|
9
|
+
<% end %>
|
data/lib/wcc.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'base64'
|
3
3
|
require 'digest/md5'
|
4
4
|
require 'erb'
|
5
|
+
require 'etc'
|
5
6
|
require 'iconv'
|
6
7
|
require 'logger'
|
7
8
|
require 'net/http'
|
@@ -18,12 +19,16 @@ require 'yaml'
|
|
18
19
|
# ruby gem dependencies
|
19
20
|
require 'diff-lcs'
|
20
21
|
require 'htmlentities'
|
22
|
+
require 'xmpp4r/client'
|
21
23
|
|
22
24
|
# wcc
|
23
25
|
require 'wcc/diff'
|
24
26
|
require 'wcc/filter'
|
25
27
|
require 'wcc/mail'
|
26
28
|
require 'wcc/site'
|
29
|
+
require 'wcc/syslog'
|
30
|
+
require 'wcc/version'
|
31
|
+
require 'wcc/xmpp'
|
27
32
|
|
28
33
|
class String
|
29
34
|
# Remove all HTML tags with at least one character name and
|
@@ -45,8 +50,6 @@ end
|
|
45
50
|
|
46
51
|
module WCC
|
47
52
|
|
48
|
-
VERSION = "1.3.0"
|
49
|
-
|
50
53
|
DIFF_TIME_FMT = '%Y-%m-%d %H:%M:%S %Z'
|
51
54
|
|
52
55
|
# logging via WCC.logger.blub
|
@@ -62,6 +65,8 @@ module WCC
|
|
62
65
|
class Conf
|
63
66
|
include Singleton
|
64
67
|
|
68
|
+
attr_reader :recipients
|
69
|
+
|
65
70
|
# use Conf like a hash containing all options
|
66
71
|
def [](key)
|
67
72
|
@options[key.to_sym] || Conf.default[key.to_sym]
|
@@ -80,12 +85,8 @@ module WCC
|
|
80
85
|
# when you want to use ./tmp it must be writeable
|
81
86
|
:cache_dir => '/var/tmp/wcc',
|
82
87
|
:tag => 'wcc',
|
83
|
-
:syslog => false,
|
84
88
|
:filter_dir => './filter.d',
|
85
89
|
:template_dir => './template.d',
|
86
|
-
:mailer => 'smtp',
|
87
|
-
:smtp_host => 'localhost',
|
88
|
-
:smtp_port => 25
|
89
90
|
}
|
90
91
|
end
|
91
92
|
|
@@ -101,10 +102,7 @@ module WCC
|
|
101
102
|
opts.on('-s', '--simulate', 'Check for update but do not save hash or diff files') do self[:simulate] = true end
|
102
103
|
opts.on('--clean', 'Remove all saved hash and diff files') do self[:clean] = true end
|
103
104
|
opts.on('-t', '--tag TAG', 'Set TAG used in output') do |t| self[:tag] = t end
|
104
|
-
opts.on('-n', '--no-mails', 'Do not
|
105
|
-
opts.on('-f', '--from MAIL', 'Set From: mail address') do |m| self[:from_mail] = m end
|
106
|
-
opts.on('--host HOST', 'Set SMTP host') do |h| self[:host] = h end
|
107
|
-
opts.on('--port PORT', 'Set SMTP port') do |p| self[:port] = p end
|
105
|
+
opts.on('-n', '--no-mails', 'Do not notify users in any way') do self[:nomails] = true end
|
108
106
|
opts.on('--show-config', 'Show config after loading config file (debug purposes)') do self[:show_config] = true end
|
109
107
|
opts.on('-h', '-?', '--help', 'Display this screen') do
|
110
108
|
puts opts
|
@@ -139,28 +137,14 @@ module WCC
|
|
139
137
|
# may be false if file is empty
|
140
138
|
yaml = YAML.load_file(self[:conf])
|
141
139
|
if yaml.is_a?(Hash) and (yaml = yaml['conf']).is_a?(Hash)
|
142
|
-
@options[:from_mail] ||= yaml['from_addr']
|
143
140
|
@options[:cache_dir] ||= yaml['cache_dir']
|
144
141
|
@options[:tag] ||= yaml['tag']
|
145
|
-
@options[:syslog] ||= yaml['use_syslog']
|
146
142
|
@options[:filter_dir] ||= yaml['filterd']
|
147
143
|
@options[:template_dir] ||= yaml['templated']
|
148
144
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
@options[:smtp_host] ||= yaml['email']['smtp']['host']
|
153
|
-
# yaml parser should provide an integer here
|
154
|
-
@options[:smtp_port] ||= yaml['email']['smtp']['port']
|
155
|
-
end
|
156
|
-
elsif yaml['email'] == 'fake_file'
|
157
|
-
@options[:mailer] = 'fake_file'
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
if self[:from_mail].to_s.empty?
|
162
|
-
WCC.logger.fatal "No sender mail address given! See help."
|
163
|
-
exit 1
|
145
|
+
MailNotificator.parse_conf(yaml['email']).each { |k,v| @options[k] ||= v }
|
146
|
+
XMPPNotificator.parse_conf(yaml['jabber']).each { |k,v| @options[k] ||= v }
|
147
|
+
SyslogNotificator.parse_conf(yaml['syslog']).each { |k,v| @options[k] ||= v }
|
164
148
|
end
|
165
149
|
|
166
150
|
if self[:show_config]
|
@@ -170,6 +154,29 @@ module WCC
|
|
170
154
|
exit 0
|
171
155
|
end
|
172
156
|
|
157
|
+
@recipients = {}
|
158
|
+
WCC.logger.debug "Load recipients from '#{self[:conf]}'"
|
159
|
+
# may be *false* if file is empty
|
160
|
+
yaml = YAML.load_file(self[:conf])
|
161
|
+
if not yaml
|
162
|
+
WCC.logger.info "No recipients loaded"
|
163
|
+
else
|
164
|
+
yaml['recipients'].to_a.each do |yaml_rec|
|
165
|
+
name = yaml_rec.keys.first
|
166
|
+
rec = []
|
167
|
+
yaml_rec[name].to_a.each do |yaml_way|
|
168
|
+
# TODO: find options and pass them to every notificator
|
169
|
+
if yaml_way.is_a?(Hash)
|
170
|
+
rec << XMPPNotificator.new(yaml_way['jabber']) if yaml_way.key?('jabber')
|
171
|
+
rec << MailNotificator.new(yaml_way['email']) if yaml_way.key?('email')
|
172
|
+
elsif yaml_way == 'syslog'
|
173
|
+
rec << SyslogNotificator.new
|
174
|
+
end
|
175
|
+
end
|
176
|
+
@recipients[name] = rec
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
173
180
|
# attach --no-mails filter
|
174
181
|
WCC::Filters.add '--no-mails' do |data|
|
175
182
|
!self[:nomails]
|
@@ -182,10 +189,8 @@ module WCC
|
|
182
189
|
@sites = []
|
183
190
|
|
184
191
|
WCC.logger.debug "Load sites from '#{Conf[:conf]}'"
|
185
|
-
|
186
192
|
# may be *false* if file is empty
|
187
193
|
yaml = YAML.load_file(Conf[:conf])
|
188
|
-
|
189
194
|
if not yaml
|
190
195
|
WCC.logger.info "No sites loaded"
|
191
196
|
return @sites
|
@@ -209,10 +214,11 @@ module WCC
|
|
209
214
|
cookie = File.open(yaml_site['cookie'], 'r') { |f| f.read }
|
210
215
|
end
|
211
216
|
|
217
|
+
|
212
218
|
@sites << Site.new(
|
213
219
|
yaml_site['url'],
|
214
220
|
yaml_site['strip_html'] || true,
|
215
|
-
yaml_site['
|
221
|
+
yaml_site['notify'] || [],
|
216
222
|
frefs,
|
217
223
|
yaml_site['auth'] || {},
|
218
224
|
cookie,
|
@@ -225,17 +231,8 @@ module WCC
|
|
225
231
|
@sites
|
226
232
|
end
|
227
233
|
|
228
|
-
def self.
|
229
|
-
return
|
230
|
-
|
231
|
-
# smtp mailer
|
232
|
-
if Conf[:mailer] == 'smtp'
|
233
|
-
@mailer = SmtpMailer.new(Conf[:smtp_host], Conf[:smtp_port])
|
234
|
-
elsif Conf[:mailer] == 'fake_file'
|
235
|
-
@mailer = FakeFileMailer.new
|
236
|
-
end
|
237
|
-
|
238
|
-
@mailer
|
234
|
+
def self.recipients
|
235
|
+
return Conf.instance.recipients
|
239
236
|
end
|
240
237
|
|
241
238
|
def self.file(path = nil) File.join(self[:cache_dir], path) end
|
@@ -332,8 +329,6 @@ module WCC
|
|
332
329
|
diff = %x[diff -U 1 --label "#{old_label}" --label "#{new_label}" #{old_site_file.path} #{Conf.file(site.id + '.site')}]
|
333
330
|
end
|
334
331
|
|
335
|
-
system("logger -t '#{Conf[:tag]}' 'Change at #{site.uri.to_s} (tag #{site.id}) detected'") if Conf[:syslog]
|
336
|
-
|
337
332
|
# construct the data made available to filters and templates
|
338
333
|
data = OpenStruct.new
|
339
334
|
data.site = site
|
@@ -343,7 +338,14 @@ module WCC
|
|
343
338
|
# HACK: there *was* an update but no notification is required
|
344
339
|
return false if not Filters.accept(data, site.filters)
|
345
340
|
|
346
|
-
|
341
|
+
site.notify.each do |name|
|
342
|
+
rec = Conf.recipients[name]
|
343
|
+
if rec.nil?
|
344
|
+
WCC.logger.error "Could not notify recipient #{name} - not found!"
|
345
|
+
else
|
346
|
+
rec.each { |way| way.notify!(data) }
|
347
|
+
end
|
348
|
+
end
|
347
349
|
|
348
350
|
true
|
349
351
|
end
|
@@ -364,6 +366,7 @@ module WCC
|
|
364
366
|
Dir.foreach(Conf[:cache_dir]) do |f|
|
365
367
|
File.delete(Conf.file(f)) if f =~ /^.*\.(md5|site)$/
|
366
368
|
end
|
369
|
+
# TODO: delete timestamps on clean?
|
367
370
|
end
|
368
371
|
|
369
372
|
# read filter.d
|
@@ -386,13 +389,6 @@ module WCC
|
|
386
389
|
@@timestamps = {}
|
387
390
|
end
|
388
391
|
|
389
|
-
# templates
|
390
|
-
@@mail_plain = load_template('mail.alt.erb')
|
391
|
-
@@mail_bodies = {
|
392
|
-
:plain => load_template('mail-body.plain.erb'),
|
393
|
-
:html => load_template('mail-body.html.erb')
|
394
|
-
}
|
395
|
-
|
396
392
|
Conf.sites.each do |site|
|
397
393
|
ts_old = get_timestamp(site)
|
398
394
|
ts_new = Time.now.to_i
|
@@ -411,10 +407,13 @@ module WCC
|
|
411
407
|
|
412
408
|
# save timestamps
|
413
409
|
File.open(cache_file, 'w+') do |f| YAML.dump({"timestamps" => @@timestamps}, f) end
|
410
|
+
|
411
|
+
# shut down notificators
|
412
|
+
MailNotificator.shut_down
|
413
|
+
XMPPNotificator.shut_down
|
414
|
+
SyslogNotificator.shut_down
|
414
415
|
end
|
415
416
|
|
416
|
-
private
|
417
|
-
|
418
417
|
def self.load_template(name)
|
419
418
|
t_path = File.join(Conf[:template_dir], name)
|
420
419
|
t = File.open(t_path, 'r') { |f| f.read }
|
@@ -422,6 +421,8 @@ module WCC
|
|
422
421
|
ERB.new(t, 0, "<>")
|
423
422
|
end
|
424
423
|
|
424
|
+
private
|
425
|
+
|
425
426
|
def self.get_timestamp(site)
|
426
427
|
@@timestamps[site.uri.to_s] || 0
|
427
428
|
end
|
data/lib/wcc/mail.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
|
2
2
|
module WCC
|
3
|
+
|
3
4
|
# An email address container with internal conversion
|
4
5
|
# routines.
|
5
6
|
class MailAddress
|
@@ -36,78 +37,116 @@ module WCC
|
|
36
37
|
|
37
38
|
def to_s; @email end
|
38
39
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
@
|
46
|
-
WCC.logger.info "Send mail via SMTP to #{@host}:#{@port}"
|
40
|
+
|
41
|
+
class MailNotificator
|
42
|
+
@@main = nil
|
43
|
+
@@bodies = nil
|
44
|
+
|
45
|
+
def initialize(opts)
|
46
|
+
@to = MailAddress.new(opts)
|
47
47
|
end
|
48
48
|
|
49
49
|
# Sends a mail built up from some [ERB] templates to the
|
50
50
|
# specified adresses.
|
51
51
|
#
|
52
|
-
# @param [
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def send(data, main, bodies, from, tos = [])
|
52
|
+
# @param [Object] data used to construct ERB binding
|
53
|
+
def notify!(data)
|
54
|
+
# from/to addresses
|
55
|
+
data.from = Conf[:from_mail]
|
56
|
+
data.to = @to
|
58
57
|
# generate a boundary that may be used for multipart
|
59
58
|
data.boundary = "frontier-#{data.site.id}"
|
60
|
-
# generate
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
bodies.each do |name,template|
|
66
|
-
data.bodies[name] = template.result(binding)
|
67
|
-
end
|
68
|
-
# eval main template
|
69
|
-
msgs[to] = main.result(binding)
|
59
|
+
# generate message
|
60
|
+
data.bodies = {}
|
61
|
+
# eval all body templates
|
62
|
+
self.class.get_bodies.each do |name,template|
|
63
|
+
data.bodies[name] = template.result(binding)
|
70
64
|
end
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
|
65
|
+
# eval main template
|
66
|
+
msg = self.class.get_main.result(binding)
|
67
|
+
|
68
|
+
case Conf[:mailer]
|
69
|
+
when 'smtp'
|
70
|
+
self.class.send_smtp(msg, Conf[:from_mail], @to, Conf[:smtp_host], Conf[:smtp_port])
|
71
|
+
when 'fake_file'
|
72
|
+
self.class.send_fake_file(msg, Conf[:from_mail], @to)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.parse_conf(conf)
|
77
|
+
if conf.is_a?(Hash)
|
78
|
+
if conf['smtp'].is_a?(Hash)
|
79
|
+
from_mail = MailAddress.new(conf['smtp']['from'] || "#{Etc.getlogin}@localhost")
|
80
|
+
return {
|
81
|
+
:mailer => 'smtp',
|
82
|
+
:from_mail => from_mails,
|
83
|
+
:smtp_host => conf['smtp']['host'] || 'localhost',
|
84
|
+
:smtp_port => conf['smtp']['port'] || 25
|
85
|
+
}
|
86
|
+
elsif conf['fake_file'].is_a?(Hash)
|
87
|
+
return {
|
88
|
+
:mailer => 'fake_file',
|
89
|
+
:from_mail => conf['fake_file']['from'] || "#{Etc.getlogin}@localhost"
|
90
|
+
}
|
75
91
|
end
|
76
92
|
end
|
77
|
-
|
78
|
-
|
93
|
+
# default is smtp
|
94
|
+
return {
|
95
|
+
:mailer => 'smtp',
|
96
|
+
:from_mail => MailAddress.new("#{Etc.getlogin}@localhost"),
|
97
|
+
:smtp_host => 'localhost',
|
98
|
+
:smtp_port => 25
|
99
|
+
}
|
79
100
|
end
|
80
|
-
|
101
|
+
|
102
|
+
def self.shut_down; end
|
81
103
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
104
|
+
# This is a specific implementation of an mail deliverer that
|
105
|
+
# does plain SMTP to host:port using [Net::SMTP].
|
106
|
+
#
|
107
|
+
# @param [String] msg the mail
|
108
|
+
# @param [MailAddress] from the From: address
|
109
|
+
# @param [MailAddress] to array of To: address
|
110
|
+
# @param [String] host the SMTP host
|
111
|
+
# @param [Integer] port the SMTP port
|
112
|
+
def self.send_smtp(msg, from, to, host, port)
|
113
|
+
# send message
|
114
|
+
Net::SMTP.start(host, port) do |smtp|
|
115
|
+
smtp.send_message(msg, from.address, to.address)
|
116
|
+
end
|
117
|
+
rescue => ex
|
118
|
+
WCC.logger.fatal "Cannot send mail via SMTP to #{host}:#{port} : #{ex}"
|
119
|
+
end
|
120
|
+
|
121
|
+
# This just dumps a mail's contents into an eml file in the current
|
122
|
+
# working directory. This should be for TESTING ONLY as it doesn't
|
123
|
+
# take care of standards and stuff like that...
|
124
|
+
#
|
125
|
+
# @param [String] msg the mail
|
126
|
+
# @param [MailAddress] from the From: address
|
127
|
+
# @param [MailAddress] to array of To: address
|
128
|
+
def self.send_fake_file(msg, from, to)
|
129
|
+
# dump mail to eml-file
|
130
|
+
filename = "#{Time.new.strftime('%Y%m%d-%H%M%S')} #{to.name}.eml"
|
131
|
+
File.open(filename, 'w') { |f| f.write(msg) }
|
88
132
|
end
|
89
133
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
msgs = {}
|
95
|
-
tos.each do |to|
|
96
|
-
data.bodies = {}
|
97
|
-
# eval all body templates
|
98
|
-
bodies.each do |name,template|
|
99
|
-
data.bodies[name] = template.result(binding)
|
100
|
-
end
|
101
|
-
# eval main template
|
102
|
-
msgs[to] = main.result(binding)
|
134
|
+
# template loading
|
135
|
+
def self.get_main
|
136
|
+
if @@main.nil?
|
137
|
+
@@main = WCC::Prog.load_template('mail.alt.erb')
|
103
138
|
end
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
139
|
+
@@main
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.get_bodies
|
143
|
+
if @@bodies.nil?
|
144
|
+
@@bodies = {
|
145
|
+
:plain => WCC::Prog.load_template('mail-body.plain.erb'),
|
146
|
+
:html => WCC::Prog.load_template('mail-body.html.erb')
|
147
|
+
}
|
110
148
|
end
|
149
|
+
@@bodies
|
111
150
|
end
|
112
151
|
end
|
113
152
|
end
|
data/lib/wcc/site.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
|
2
2
|
module WCC
|
3
3
|
class Site
|
4
|
-
attr_reader :uri, :
|
4
|
+
attr_reader :uri, :notify, :filters, :auth, :cookie, :check_interval, :id
|
5
5
|
|
6
|
-
def initialize(url, strip_html,
|
6
|
+
def initialize(url, strip_html, notify, filters, auth, cookie, check_interval)
|
7
7
|
@uri = URI.parse(url)
|
8
8
|
@strip_html = strip_html
|
9
|
-
@
|
9
|
+
@notify = notify.is_a?(Array) ? notify : [notify]
|
10
10
|
@filters = filters.is_a?(Array) ? filters : [filters]
|
11
11
|
@auth = auth
|
12
12
|
@cookie = cookie
|
data/lib/wcc/syslog.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
module WCC
|
3
|
+
class SyslogNotificator
|
4
|
+
def initialize
|
5
|
+
end
|
6
|
+
|
7
|
+
def notify!(data)
|
8
|
+
system("logger -t '#{data.tag}' 'Change at #{data.site.uri.to_s} (tag #{data.site.id}) detected'")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.parse_conf(conf); {} end
|
12
|
+
|
13
|
+
def self.shut_down; end
|
14
|
+
end
|
15
|
+
end
|
data/lib/wcc/version.rb
ADDED
data/lib/wcc/xmpp.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
|
2
|
+
module WCC
|
3
|
+
class XMPPNotificator
|
4
|
+
@@client = nil
|
5
|
+
@@template = nil
|
6
|
+
|
7
|
+
def initialize(opts)
|
8
|
+
@jid = Jabber::JID.new(opts)
|
9
|
+
end
|
10
|
+
|
11
|
+
def notify!(data)
|
12
|
+
# prepare message
|
13
|
+
subject = "[#{data.tag}] #{data.site.uri.host} changed"
|
14
|
+
body = self.class.get_template.result(binding)
|
15
|
+
m = Jabber::Message.new(@jid, body)
|
16
|
+
m.type = :normal
|
17
|
+
m.subject = subject
|
18
|
+
# send it
|
19
|
+
c = self.class.get_client
|
20
|
+
c.send(m) unless c.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.parse_conf(conf)
|
24
|
+
if conf.is_a?(Hash)
|
25
|
+
if conf['jid'].nil?
|
26
|
+
WCC.logger.fatal "Missing jabber ID!"
|
27
|
+
return {:xmpp_jid => nil}
|
28
|
+
elsif conf['password'].nil?
|
29
|
+
WCC.logger.fatal "Missing jabber password!"
|
30
|
+
else
|
31
|
+
return {
|
32
|
+
:xmpp_jid => Jabber::JID.new(conf['jid']),
|
33
|
+
:xmpp_password => conf['password']
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.shut_down
|
40
|
+
if not @@client.nil?
|
41
|
+
#@@client.send(Jabber::Presence.new.set_type(:unavailable))
|
42
|
+
@@client.close
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.get_client
|
47
|
+
if @@client.nil? and not Conf[:xmpp_jid].nil?
|
48
|
+
@@client = Jabber::Client.new(Conf[:xmpp_jid])
|
49
|
+
@@client.connect
|
50
|
+
begin
|
51
|
+
@@client.auth(Conf[:xmpp_password])
|
52
|
+
@@client.send(Jabber::Presence.new.set_status('At your service every night.'))
|
53
|
+
rescue Jabber::ClientAuthenticationFailure => ex
|
54
|
+
WCC.logger.fatal "Wrong jabber password for #{Conf[:xmpp_jid]}!"
|
55
|
+
@@client.close
|
56
|
+
@@client = nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
@@client
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.get_template
|
63
|
+
if @@template.nil?
|
64
|
+
@@template = WCC::Prog.load_template('xmpp-body.plain.erb')
|
65
|
+
end
|
66
|
+
@@template
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
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: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
-
-
|
8
|
-
- 3
|
7
|
+
- 2
|
9
8
|
- 0
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 2.0.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-10-
|
18
|
+
date: 2011-10-16 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: htmlentities
|
@@ -23,18 +23,34 @@ dependencies:
|
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - ~>
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
hash:
|
28
|
+
hash: 29
|
29
29
|
segments:
|
30
|
-
-
|
31
|
-
|
30
|
+
- 4
|
31
|
+
- 3
|
32
|
+
version: "4.3"
|
32
33
|
type: :runtime
|
33
34
|
version_requirements: *id001
|
34
35
|
- !ruby/object:Gem::Dependency
|
35
36
|
name: diff-lcs
|
36
37
|
prerelease: false
|
37
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 13
|
44
|
+
segments:
|
45
|
+
- 1
|
46
|
+
- 1
|
47
|
+
version: "1.1"
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: xmpp4r
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
38
54
|
none: false
|
39
55
|
requirements:
|
40
56
|
- - ">="
|
@@ -44,8 +60,8 @@ dependencies:
|
|
44
60
|
- 0
|
45
61
|
version: "0"
|
46
62
|
type: :runtime
|
47
|
-
version_requirements: *
|
48
|
-
description: wcc tracks changes of websites and notifies you by email.
|
63
|
+
version_requirements: *id003
|
64
|
+
description: wcc tracks changes of websites and notifies you by email and as of version 2.0 via XMPP/Jabber too.
|
49
65
|
email: chrnicolai@gmail.com
|
50
66
|
executables:
|
51
67
|
- wcc
|
@@ -70,6 +86,7 @@ files:
|
|
70
86
|
- assets/template.d/mail-body.html.erb
|
71
87
|
- assets/template.d/mail-body.plain.erb
|
72
88
|
- assets/template.d/mail.plain.erb
|
89
|
+
- assets/template.d/xmpp-body.plain.erb
|
73
90
|
- bin/wcc
|
74
91
|
- bin/wcc-init
|
75
92
|
- bin/wcc-upgrade
|
@@ -78,6 +95,9 @@ files:
|
|
78
95
|
- lib/wcc/filter.rb
|
79
96
|
- lib/wcc/mail.rb
|
80
97
|
- lib/wcc/site.rb
|
98
|
+
- lib/wcc/syslog.rb
|
99
|
+
- lib/wcc/version.rb
|
100
|
+
- lib/wcc/xmpp.rb
|
81
101
|
- lib/wcc.rb
|
82
102
|
- LICENSE
|
83
103
|
- README.md
|