webwatchr 0.0.2

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.
@@ -0,0 +1,119 @@
1
+ require "fileutils"
2
+
3
+ module Webwatchr
4
+ require_relative "alerting"
5
+ class Main
6
+ include Loggable
7
+
8
+ def initialize(&block)
9
+ @alerts = []
10
+ super()
11
+ setup_logs
12
+ run!
13
+ instance_eval(&block)
14
+ logger.info("Webwatcher finished working")
15
+ end
16
+
17
+ def running?
18
+ return (File.exist?(PARAMS[:pid_file]) and not PARAMS[:site])
19
+ end
20
+
21
+ def set(key, val)
22
+ PARAMS[key] = val
23
+ self
24
+ end
25
+
26
+ def setup_logs
27
+ FileUtils.mkdir_p(PARAMS[:log_dir])
28
+ log_out_file = if PARAMS[:verbose] || PARAMS[:test]
29
+ $stdout
30
+ else
31
+ File.join(PARAMS[:log_dir], 'webwatchr.log')
32
+ end
33
+ log_out_file_rotation = 'weekly'
34
+ log_level = $VERBOSE ? Logger::DEBUG : Logger::INFO
35
+
36
+ MyLog.instance.configure(log_out_file, log_out_file_rotation, log_level)
37
+ end
38
+
39
+ def init()
40
+ logger.debug("Starting WebWatchr")
41
+
42
+ FileUtils.mkdir_p(PARAMS[:last_dir])
43
+ FileUtils.mkdir_p(PARAMS[:cache_dir])
44
+
45
+ Dir[File.join(__dir__, '..', 'sites', '*.rb')].sort.each do |site_path|
46
+ logger.debug("Loading #{site_path}")
47
+ require site_path
48
+ end
49
+ end
50
+
51
+ def update(site_class, &block)
52
+ if (PARAMS[:mode] == :single) && site_class.to_s != PARAMS[:site]
53
+ logger.info("Running in single site mode, skipping #{site_class} (!= #{PARAMS[:site]})")
54
+ return
55
+ end
56
+
57
+ site = site_class.create(&block)
58
+
59
+ site.alerters = @alerts
60
+
61
+ logger.info "Running #{site.name}"
62
+ if block
63
+ site.instance_eval(&block)
64
+ end
65
+ Timeout.timeout(PARAMS[:site_timeout]) {
66
+ site.update(test: PARAMS[:test], cache_dir: PARAMS[:cache_dir], last_dir: PARAMS[:last_dir])
67
+ }
68
+ rescue Net::OpenTimeout, Errno::ENETUNREACH, Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Zlib::BufError, Errno::ECONNREFUSED, SocketError, Net::ReadTimeout => e
69
+ logger.warn "Failed pulling #{site}: #{e.message}"
70
+ # Do nothing, try later
71
+ rescue SystemExit => e
72
+ msg = "User requested we quit while updating #{site}\n"
73
+ logger.error msg
74
+ warn msg
75
+ raise e
76
+ rescue StandardError => e
77
+ msg = "Issue with #{site_class} : #{e}\n"
78
+ msg += "#{e.message}\n"
79
+ logger.error msg
80
+ msg += e.backtrace.join("\n")
81
+ logger.debug e.backtrace.join("\n")
82
+ warn msg
83
+ end
84
+
85
+ def run!
86
+ if running?
87
+ logger.info "Already running. Quitting"
88
+ exit
89
+ end
90
+
91
+ begin
92
+ File.open(PARAMS[:pid_file], 'w+') { |f|
93
+ f.puts($$)
94
+ init()
95
+ }
96
+ ensure
97
+ if File.exist?(PARAMS[:pid_file])
98
+ FileUtils.rm PARAMS[:pid_file]
99
+ end
100
+ end
101
+ end
102
+
103
+ def add_default_alert(type, &block)
104
+ case type
105
+ when :email
106
+ alert = Alerting::EmailAlert.create(&block)
107
+ @alerts.append(alert)
108
+ when :telegram
109
+ alert = Alerting::TelegramAlert.create(&block)
110
+ @alerts.append(alert)
111
+ when :stdout
112
+ alert = Alerting::StdoutAlert.create()
113
+ @alerts.append(alert)
114
+ else
115
+ raise StandardError, "Unknown alert type: #{type}."
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,31 @@
1
+ require 'logger'
2
+ require 'singleton'
3
+
4
+ class MyLog
5
+ include Singleton
6
+
7
+ def initialize
8
+ @many_loggers = {}
9
+ @default_level = Logger::DEBUG
10
+ @default_out = $stdout
11
+ end
12
+
13
+ def logger(class_name)
14
+ unless @many_loggers[class_name]
15
+ @many_loggers[class_name] = Logger.new(@default_out, @default_rotation, level: @default_level, progname: class_name)
16
+ end
17
+ return @many_loggers[class_name]
18
+ end
19
+
20
+ def configure(out, rotation, level)
21
+ @default_out = out
22
+ @default_rotation = rotation
23
+ @default_level = level
24
+ end
25
+ end
26
+
27
+ module Loggable
28
+ def logger
29
+ MyLog.instance.logger(self.class.name)
30
+ end
31
+ end
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require "optparse"
4
+
5
+ require_relative "logger"
6
+
7
+ $: << "./lib/" # for telegram to load
8
+
9
+ trap("INT") do
10
+ warn "User interrupted"
11
+ exit
12
+ end
13
+
14
+ module Webwatchr
15
+ include Loggable
16
+
17
+ PARAMS = { mode: :normal, test: false } # rubocop:disable Style/MutableConstant
18
+
19
+ if ARGV.any?
20
+ OptionParser.new { |o|
21
+ o.banner = "WebWatchr is a script to poll websites and alert on changes.
22
+ Exemple uses:
23
+ * Updates all webpages according to their 'wait' value, and compare against internal state, and update it.
24
+ ruby #{__FILE__}
25
+ * Updates sites-available/site.rb, ignoring 'wait' value, and compare against internal state, and update it.
26
+ ruby #{__FILE__} -s site.rb
27
+
28
+ Usage: ruby #{__FILE__} "
29
+ o.on("-sSITE", "--site=SITE", "Run WebWatcher on one site only. It has to be the name of the class for that site.") do |val|
30
+ PARAMS[:site] = val
31
+ PARAMS[:mode] = :single
32
+ end
33
+ o.on("-v", "--verbose", "Be verbose (output to STDOUT instead of logfile") do
34
+ PARAMS[:verbose] = true
35
+ end
36
+ o.on("-t", "--test", "Check website and return what we've parsed") do
37
+ PARAMS[:test] = true
38
+ end
39
+ o.on("-h", "--help", "Prints this help") {
40
+ puts o
41
+ exit
42
+ }
43
+ }.parse!()
44
+ end
45
+
46
+ PARAMS[:cache_dir] = File.join(__dir__, "..", "..", ".cache")
47
+ PARAMS[:last_dir] = File.join(__dir__, "..", "..", ".lasts")
48
+ PARAMS[:log_dir] = File.join(__dir__, "..", "..", "logs")
49
+ PARAMS[:pid_file] = File.join(__dir__, "..", "..", "webwatchr.pid")
50
+ require "webwatchr/base"
51
+ end