omnibot 0.0.21 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/examples/command_mail.rb +2 -2
- data/lib/omnibot.rb +32 -33
- data/lib/omnibot/amqpconsumer.rb +72 -72
- data/lib/omnibot/helpers.rb +64 -64
- data/lib/omnibot/jabberbot.rb +198 -205
- data/lib/omnibot/launcher.rb +58 -69
- data/lib/omnibot/loggedcommand.rb +14 -15
- data/lib/omnibot/mailchecker.rb +106 -114
- data/lib/omnibot/omnisend.rb +19 -21
- data/lib/omnibot/periodiccommand.rb +40 -45
- data/lib/omnibot/version.rb +1 -1
- metadata +20 -5
data/lib/omnibot/launcher.rb
CHANGED
@@ -1,83 +1,72 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module OmniBot
|
4
|
+
# entry point
|
5
|
+
class Launcher
|
6
|
+
def get_config_path(args)
|
7
|
+
return args[0] if args[0] && File.file?(args[0])
|
8
|
+
path = ENV['HOME'] + '/.local/share/omnibot/omnibot.yaml'
|
9
|
+
return path if File.file?(path)
|
10
|
+
raise 'No config file found, checked command line and ~/.local/share/omnibot/omnibot.yaml'
|
11
|
+
end
|
4
12
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
return path if ENV['HOME'] && File.file?(path)
|
11
|
-
raise 'No config file found, checked command line and ~/.omnibot.yaml'
|
12
|
-
end
|
13
|
+
def ensure_omnidir
|
14
|
+
path = ENV['HOME'] + '/.local/share/omnibot'
|
15
|
+
FileUtils.mkdir path unless File.directory? path
|
16
|
+
path
|
17
|
+
end
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
+
def log_path(config)
|
20
|
+
return config['logpath'] unless (config['logpath'] || '').empty?
|
21
|
+
ensure_omnidir + '/omnibot.log'
|
22
|
+
end
|
19
23
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
def create_db
|
25
|
+
db = SQLite3::Database.new(ensure_omnidir + '/omnibot.sqlite3')
|
26
|
+
if db.execute("select * from sqlite_master where type='table' and name='received_messages'").empty?
|
27
|
+
db.execute <<-SQL
|
28
|
+
create table received_messages (
|
29
|
+
account TEXT,
|
30
|
+
message TEXT,
|
31
|
+
date TEXT
|
32
|
+
);
|
33
|
+
SQL
|
34
|
+
end
|
35
|
+
db
|
36
|
+
end
|
24
37
|
|
25
|
-
|
26
|
-
|
27
|
-
if db.execute("select * from sqlite_master where type='table' and name='received_messages'").empty?
|
28
|
-
db.execute <<-SQL
|
29
|
-
create table received_messages (
|
30
|
-
account TEXT,
|
31
|
-
message TEXT,
|
32
|
-
date TEXT
|
33
|
-
);
|
34
|
-
SQL
|
35
|
-
end
|
36
|
-
db
|
37
|
-
end
|
38
|
+
def provide_handlers(config, db)
|
39
|
+
periodic_commands = [config['periodiccommands']].flatten.compact
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
used_mails = mails.select { |m| mail_triggers.find { |mt| m['user'] == mt['for'] } }
|
49
|
-
raise 'Sorry but multiple mail addresses is not supported yet' if used_mails.size > 1
|
41
|
+
mails = [config['mails']].flatten.compact
|
42
|
+
mail_triggers = [config['mailtriggers']].flatten.compact
|
43
|
+
mail_triggers.each do |mt|
|
44
|
+
raise 'No mail found for a trigger' unless mails.find { |m| m['user'] == mt['for'] }
|
45
|
+
raise 'Not supported action' unless mt['action'] == 'unpack'
|
46
|
+
end
|
47
|
+
used_mails = mails.select { |m| mail_triggers.find { |mt| m['user'] == mt['for'] } }
|
48
|
+
raise 'Sorry but multiple mail addresses is not supported yet' if used_mails.size > 1
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
mail_triggers.map do |trigger|
|
56
|
-
mail = mails.find { |m| m['user'] == trigger['for'] }
|
57
|
-
MailChecker.new mail, trigger, db
|
58
|
-
end
|
59
|
-
end
|
50
|
+
[] +
|
51
|
+
periodic_commands.map { |command| PeriodicCommand.new command } +
|
52
|
+
mail_triggers.map { |trigger| MailChecker.new(mails.find { |m| m['user'] == trigger['for'] }, trigger, db) }
|
53
|
+
end
|
60
54
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
55
|
+
def start(args)
|
56
|
+
config_path = get_config_path args
|
57
|
+
puts "Using config at #{config_path}"
|
58
|
+
config = YAML.load_file(config_path)['config']
|
65
59
|
|
66
|
-
|
67
|
-
|
68
|
-
OmniLog::log = Logger.new(log_path)
|
69
|
-
OmniLog::log.level = Logger::DEBUG
|
60
|
+
OmniLog::log = Logger.new(log_path(config))
|
61
|
+
OmniLog::log.level = Logger::DEBUG
|
70
62
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
63
|
+
db = create_db
|
64
|
+
consumer = AMQPConsumer.new config
|
65
|
+
consumer.db = db
|
66
|
+
consumer.handlers = provide_handlers(config, db)
|
67
|
+
consumer.start
|
76
68
|
|
77
|
-
|
78
|
-
|
79
|
-
|
69
|
+
OmniLog::log.close
|
70
|
+
end
|
71
|
+
end
|
80
72
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
@@ -1,20 +1,19 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module OmniBot
|
4
|
-
|
4
|
+
module LoggedCommand
|
5
|
+
def jabber_logged_command(banner, command)
|
6
|
+
body = `#{command}`
|
7
|
+
if $? != 0
|
8
|
+
@jabber_messenger.call "#{banner} command #{command} failed with an error #{$?}:\n" + body
|
9
|
+
end
|
10
|
+
if body.strip != ''
|
11
|
+
@jabber_messenger.call "#{banner} command #{command} succeeded with:\n" + body
|
12
|
+
end
|
13
|
+
end
|
5
14
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
if body.strip != ''
|
12
|
-
@jabber_messenger.call "#{banner} command #{command} succeeded with:\n" + body
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def set_jabber_messenger &block
|
17
|
-
@jabber_messenger = block
|
18
|
-
end
|
19
|
-
end
|
15
|
+
def jabber_messenger(&block)
|
16
|
+
@jabber_messenger = block
|
17
|
+
end
|
18
|
+
end
|
20
19
|
end
|
data/lib/omnibot/mailchecker.rb
CHANGED
@@ -1,118 +1,110 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module OmniBot
|
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
|
-
end
|
111
|
-
|
112
|
-
def start
|
113
|
-
@timer_provider.add_timer(@startup_pause) { on_first_timer }
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
4
|
+
class MailChecker
|
5
|
+
include LoggedCommand
|
6
|
+
|
7
|
+
def on_first_timer
|
8
|
+
on_periodic_timer
|
9
|
+
@timer_provider.add_periodic_timer(3600) { on_periodic_timer }
|
10
|
+
end
|
11
|
+
|
12
|
+
def match_condition(m, condition_name, mail_name = condition_name)
|
13
|
+
values = m.send(mail_name)
|
14
|
+
[values].flatten.any? do |value|
|
15
|
+
@conditions[condition_name] && Regexp.new(@conditions[condition_name]).match(value.to_s)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def match_conditions(m)
|
20
|
+
@conditions.empty? || %w( subject from to cc date).any? { |condition| match_condition m, condition }
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_periodic_timer
|
24
|
+
OmniLog::info "Checking mail #{@address}"
|
25
|
+
is_new_mail = false
|
26
|
+
Mail.all.each do |m|
|
27
|
+
rows = @db.execute 'select message from received_messages where account=? and message=?', @address, m.message_id
|
28
|
+
|
29
|
+
next unless rows.empty?
|
30
|
+
is_new_mail = true
|
31
|
+
OmniLog::info "New message from #{m.from} about #{m.subject}; id #{m.message_id}"
|
32
|
+
handle_message(m) if match_conditions(m)
|
33
|
+
@db.execute 'insert into received_messages values(?, ?, ?)', @address, m.message_id, m.date.to_s
|
34
|
+
end
|
35
|
+
OmniLog::info 'No new mail' unless is_new_mail
|
36
|
+
rescue => e
|
37
|
+
OmniLog::error "MailChecker error: #{e.message}\ntrace:\n#{Helpers::backtrace e}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def handle_message(m)
|
41
|
+
OmniLog::info 'Matched ' + m.inspect.to_s
|
42
|
+
attached = m.attachments.find { |a| a.mime_type =~ %r{application/(zip|x-zip|rar|x-rar).*} }
|
43
|
+
if attached
|
44
|
+
Dir.mktmpdir('omniatt') do |tmpdir|
|
45
|
+
filename = tmpdir + '/' + attached.filename
|
46
|
+
OmniLog::info "Writing attachment to #{filename}"
|
47
|
+
File.open(filename, 'w') { |f| f.write attached.read }
|
48
|
+
Dir.chdir(@unpack_to) do
|
49
|
+
if filename =~ /\.zip$/
|
50
|
+
system("unzip -oq '#{filename}'")
|
51
|
+
elsif filename =~ /\.rar$/
|
52
|
+
system("unrar x -y '#{filename}'")
|
53
|
+
else
|
54
|
+
raise 'Wrong filetype'
|
55
|
+
end
|
56
|
+
raise "Error extracting file #{filename} to #{@unpack_to}" if $? != 0
|
57
|
+
end
|
58
|
+
|
59
|
+
message_body = "Received an email '#{m.subject}' from '#{m.from.join(',')}' with "\
|
60
|
+
"an attachment #{attached.filename}. Successfully extracted an attachment to #{@unpack_to}."
|
61
|
+
@jabber_messenger.call message_body
|
62
|
+
|
63
|
+
jabber_logged_command 'Mail post-receive ', "#{@command_post} #{filename} #{@unpack_to}"
|
64
|
+
end
|
65
|
+
else
|
66
|
+
OmniLog::info 'No attachment found'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def yaml_to_mailhash(yaml_config)
|
71
|
+
{ address: yaml_config['host'],
|
72
|
+
port: yaml_config['port'],
|
73
|
+
user_name: yaml_config['user'],
|
74
|
+
password: yaml_config['password'],
|
75
|
+
enable_ssl: yaml_config['ssl']
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
public
|
80
|
+
|
81
|
+
attr_writer :timer_provider
|
82
|
+
attr_writer :startup_pause
|
83
|
+
|
84
|
+
def initialize(mail_config, trigger_config, db)
|
85
|
+
@startup_pause = 0
|
86
|
+
@mail_config = mail_config
|
87
|
+
@db = db
|
88
|
+
@conditions = (trigger_config['if'] || {})
|
89
|
+
@unpack_to = trigger_config['unpack_to']
|
90
|
+
@command_post = trigger_config['command_post']
|
91
|
+
@address = trigger_config['for']
|
92
|
+
|
93
|
+
mailhash = yaml_to_mailhash(mail_config)
|
94
|
+
Mail.defaults do
|
95
|
+
retriever_method :pop3, mailhash
|
96
|
+
end
|
97
|
+
|
98
|
+
raise 'Wrong command' if (@command_post || '') == ''
|
99
|
+
raise 'No dir to extract to' unless File.directory? @unpack_to
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_s
|
103
|
+
"Mail checker for #{@address}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def start
|
107
|
+
@timer_provider.add_timer(@startup_pause) { on_first_timer }
|
108
|
+
end
|
109
|
+
end
|
117
110
|
end
|
118
|
-
|
data/lib/omnibot/omnisend.rb
CHANGED
@@ -1,27 +1,25 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module OmniBot
|
4
|
+
class OmniSend
|
5
|
+
def start(args)
|
6
|
+
if args.empty?
|
7
|
+
message = STDIN.readlines.join
|
8
|
+
else
|
9
|
+
message = args.join(' ')
|
10
|
+
end
|
11
|
+
puts "Sending message #{message}"
|
12
|
+
data = Base64.encode64(Marshal.dump(message))
|
4
13
|
|
5
|
-
|
14
|
+
Signal.trap('INT') { AMQP.stop { EM.stop } }
|
6
15
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
Signal.trap('INT') { AMQP.stop{ EM.stop } }
|
17
|
-
|
18
|
-
AMQP.start do |connection|
|
19
|
-
mq = AMQP::Channel.new(connection)
|
20
|
-
exchange = mq.direct(Helpers::amqp_exchange_name)
|
21
|
-
exchange.publish(data, :routing_key => Helpers::amqp_routing_key)
|
22
|
-
EM.add_timer(2.0) { connection.close { puts 'sent' ; EM.stop } }
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
16
|
+
AMQP.start do |connection|
|
17
|
+
mq = AMQP::Channel.new(connection)
|
18
|
+
exchange = mq.direct(Helpers::amqp_exchange_name)
|
19
|
+
exchange.publish(data, routing_key: Helpers::amqp_routing_key)
|
20
|
+
EM.add_timer(2.0) { connection.close { EM.stop } }
|
21
|
+
end
|
22
|
+
puts 'Sent'
|
23
|
+
end
|
24
|
+
end
|
26
25
|
end
|
27
|
-
|
@@ -1,49 +1,44 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module OmniBot
|
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
|
-
@timer_provider.add_timer(next_report_time - now) { on_first_timer }
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
4
|
+
# Send to jabber user result of a daily command
|
5
|
+
class PeriodicCommand
|
6
|
+
include OmniBot::LoggedCommand
|
7
|
+
|
8
|
+
def on_first_timer
|
9
|
+
on_periodic_timer
|
10
|
+
@timer_provider.add_periodic_timer(24 * 3600) { on_periodic_timer }
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_periodic_timer
|
14
|
+
OmniLog::info "Reporting command #{@command}"
|
15
|
+
jabber_logged_command 'Periodic command', @command
|
16
|
+
rescue => e
|
17
|
+
OmniLog::error "PeriodicCommand error: #{e.message}\ntrace:\n#{Helpers::backtrace e}"
|
18
|
+
end
|
19
|
+
|
20
|
+
public
|
21
|
+
|
22
|
+
attr_writer :timer_provider
|
23
|
+
attr_writer :startup_pause
|
24
|
+
|
25
|
+
def initialize(command)
|
26
|
+
@command = command
|
27
|
+
@startup_pause = 0
|
28
|
+
|
29
|
+
raise 'Wrong command' if (@command || '') == ''
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
"Periodic command '#{@command}'"
|
34
|
+
end
|
35
|
+
|
36
|
+
def start
|
37
|
+
now = Time.now
|
38
|
+
tomorrow = DateTime.now + 1
|
39
|
+
next_report_time = Time.local(tomorrow.year, tomorrow.month, tomorrow.day, 1, 0, 0)
|
40
|
+
next_report_time += @startup_pause
|
41
|
+
@timer_provider.add_timer(next_report_time - now) { on_first_timer }
|
42
|
+
end
|
43
|
+
end
|
48
44
|
end
|
49
|
-
|