popper 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e3e80b9450b5e0a70fca15e4abc2dc3a39e11e9
4
- data.tar.gz: 82a664876f10f85daa0ae18dbf42445b1730d498
3
+ metadata.gz: 4c3c075a3c44288b2c8f7ed3e5dd890ef946f2bd
4
+ data.tar.gz: b60b17cfa8c19e4fda90dd8d6ab9639072744fb6
5
5
  SHA512:
6
- metadata.gz: 4770a0411386ba1857f7b32c5675f0c84c5a99b3b3d83592e2913ec3efb5b07f6c950b73de69f8c676f8e32cdbbe43aab4b0bbbcad8c82726a43dd2151c44b13
7
- data.tar.gz: 9117907fa16d7c0592106b3af4bd887b5591d669feaeb12d0ad465fde7604db66f79aef21f0b05f8260dd1e4de93d1e876ff2d33e40f2c2fe8fd994adb0bc79e
6
+ metadata.gz: 15c20311b6c773f3744f99c7909dbda33a3df4cca2dd2b49fac0af3196e65426494ff70ee18828ceca365af157dd492d174c315004bb4f0b9e280636cc6dd5b9
7
+ data.tar.gz: b8abc1581a23478b5806580dcce98660c9135d7da6e08bf90b21c1e198504019b666983d525f3c49a145714c06e42db7d581b8bdb556d6f3140c6a15d9b58437
data/README.md CHANGED
@@ -13,26 +13,25 @@ To post a variety of services by analyzing the email
13
13
 
14
14
  # usage
15
15
  ```
16
- # create ~/popper/popper.conf
16
+ # create /etc/popper.conf
17
17
  $ popper init
18
18
 
19
19
  # edit popper.conf
20
- $ vi ~/popper/popper.conf
20
+ $ vi /etc/popper.conf
21
21
 
22
- # pop uidl prefetch
23
- # to avoid duplication and to fetch the uidl
24
- $ popper prepop
22
+ # print config
23
+ $ popper print
25
24
 
26
- $ popper
27
- ```
28
- `crontab -l`
29
- ```
30
- * * * * * /path/to/popper
25
+ $ popper --daemon --config /etc/popper.conf --log /var/log/popper.log --pidfile /var/run/popper/popper.pid
31
26
  ```
27
+ systmd service config: https://github.com/pyama86/popper/tree/master/init_script/cent7/etc/systemd/system/popper.service
32
28
 
33
29
  # configure(toml)
34
30
  ## ~/popper/popper.conf
35
31
  ```
32
+ [global]
33
+ interval = 60 # fetch interbal default:60
34
+
36
35
  [default.condition]
37
36
 
38
37
  subject = ["^(?!.*Re:).+$"]
@@ -82,8 +81,12 @@ user = "example2@example.com"
82
81
  ```
83
82
 
84
83
  # option
85
- * config_file `--config or -c`
86
- * log_file(default=/var/log/popper.log) `--log or -l`
84
+ ```
85
+ -c, [--config=CONFIG]
86
+ -l, [--log=LOG]
87
+ -d, [--daemon], [--no-daemon]
88
+ -p, [--pidfile=PIDFILE]
89
+ ```
87
90
 
88
91
  # author
89
92
  * pyama
@@ -0,0 +1,17 @@
1
+ [Unit]
2
+ Description=Popper
3
+ Requires=network.target
4
+ After=network.target
5
+
6
+ [Service]
7
+ Type=forking
8
+ User=root
9
+
10
+ Restart=always
11
+ RestartSec=120
12
+
13
+ ExecStart=/usr/local/bin/popper --daemon --config /etc/popper.conf --log /var/log/popper.log --pidfile /var/run/popper/popper.pid
14
+ PIDFile=/var/run/popper/popper.pid
15
+
16
+ [Install]
17
+ WantedBy=multi-user.target
@@ -1,17 +1,18 @@
1
1
  require 'pp'
2
2
  require "popper/version"
3
3
  require "popper/cli"
4
- require 'popper/pop'
4
+ require 'popper/mail_account'
5
5
  require "popper/config"
6
6
  require "popper/action"
7
7
  require "popper/init"
8
- require "popper/sync"
9
8
 
10
9
  module Popper
11
10
  def self.init_logger(options, stdout=nil)
12
- log_path = options[:log] || File.join(Dir.home, "popper", "popper.log")
11
+ log_path = options[:log] || "/var/log/popper.log"
13
12
  log_path = STDOUT if ENV["POPPER_TEST"] || stdout
14
13
  @_logger = Logger.new(log_path)
14
+ rescue => e
15
+ puts e
15
16
  end
16
17
 
17
18
  def self.log
@@ -2,14 +2,14 @@ module Popper::Action
2
2
  class Base
3
3
  @next_action = nil
4
4
  @action = nil
5
- @_config = nil
5
+ @action_config = nil
6
6
 
7
7
  def self.run(config, mail, params={})
8
- set_config(config)
8
+ @action_config = config.send(self.action) if config.respond_to?(self.action)
9
9
  if action?
10
10
  begin
11
11
  Popper.log.info "run action #{self.action}"
12
- params = task(config, mail, params)
12
+ params = task(mail, params)
13
13
  Popper.log.info "exit action #{self.action}"
14
14
  rescue => e
15
15
  Popper.log.warn e
@@ -34,17 +34,8 @@ module Popper::Action
34
34
  end
35
35
 
36
36
  def self.action?
37
- my_config && check_params
37
+ @action_config && check_params
38
38
  end
39
-
40
- def self.set_config(config)
41
- @_config = config.send(self.action) if config.respond_to?(self.action)
42
- end
43
-
44
- def self.my_config
45
- @_config
46
- end
47
-
48
- def self.check_params(config); end
39
+ def self.check_params; end
49
40
  end
50
41
  end
@@ -5,14 +5,14 @@ module Popper::Action
5
5
  def self.octkit
6
6
  Octokit.reset!
7
7
  Octokit.configure do |c|
8
- c.web_endpoint = my_config.url
9
- c.api_endpoint = File.join(my_config.url, "api/v3")
8
+ c.web_endpoint = @action_config.url
9
+ c.api_endpoint = File.join(@action_config.url, "api/v3")
10
10
  end
11
- Octokit::Client.new(:access_token => my_config.token)
11
+ Octokit::Client.new(:access_token => @action_config.token)
12
12
  end
13
13
 
14
14
  def self.check_params
15
- my_config.respond_to?(:url) && super
15
+ @action_config.respond_to?(:url) && super
16
16
  end
17
17
 
18
18
  next_action(Slack)
@@ -2,9 +2,9 @@
2
2
  require 'octokit'
3
3
  module Popper::Action
4
4
  class Git < Base
5
- def self.task(config, mail, params={})
5
+ def self.task(mail, params={})
6
6
  url = octkit.create_issue(
7
- my_config.repo,
7
+ @action_config.repo,
8
8
  mail.subject,
9
9
  mail.body
10
10
  )
@@ -14,12 +14,12 @@ module Popper::Action
14
14
 
15
15
  def self.octkit
16
16
  Octokit.reset!
17
- Octokit::Client.new(:access_token => my_config.token)
17
+ Octokit::Client.new(:access_token => @action_config.token)
18
18
  end
19
19
 
20
20
  def self.check_params
21
- my_config.respond_to?(:repo) &&
22
- my_config.respond_to?(:token)
21
+ @action_config.respond_to?(:repo) &&
22
+ @action_config.respond_to?(:token)
23
23
  end
24
24
 
25
25
  next_action(Ghe)
@@ -1,11 +1,11 @@
1
1
  require 'slack-notifier'
2
2
  module Popper::Action
3
3
  class Slack < Base
4
- def self.task(config, mail, params={})
4
+ def self.task(mail, params={})
5
5
  notifier = ::Slack::Notifier.new(
6
- my_config.webhook_url,
7
- channel: my_config.channel,
8
- username: my_config.user || 'popper',
6
+ @action_config.webhook_url,
7
+ channel: @action_config.channel,
8
+ username: @action_config.user || 'popper',
9
9
  link_names: 1
10
10
  )
11
11
 
@@ -15,8 +15,8 @@ module Popper::Action
15
15
  color: "good"
16
16
  }
17
17
 
18
- body = my_config.message || "popper mail notification"
19
- body += " #{my_config.mentions.join(" ")}" if my_config.mentions
18
+ body = @action_config.message || "popper mail notification"
19
+ body += " #{@action_config.mentions.join(" ")}" if @action_config.mentions
20
20
  %w(
21
21
  git
22
22
  ghe
@@ -28,8 +28,8 @@ module Popper::Action
28
28
  end
29
29
 
30
30
  def self.check_params
31
- my_config.respond_to?(:channel) &&
32
- my_config.respond_to?(:webhook_url)
31
+ @action_config.respond_to?(:channel) &&
32
+ @action_config.respond_to?(:webhook_url)
33
33
  end
34
34
 
35
35
  action(:slack)
@@ -5,22 +5,39 @@ module Popper
5
5
  class CLI < Thor
6
6
  class_option :config, type: :string, aliases: '-c'
7
7
  class_option :log, type: :string, aliases: '-l'
8
+ class_option :daemon, type: :boolean, aliases: '-d'
9
+ class_option :pidfile, type: :string, aliases: '-p'
8
10
  default_task :pop
9
11
  desc "pop", "from pop3"
10
12
  def pop
13
+ if(options[:daemon])
14
+ Popper.init_logger(options)
15
+ Process.daemon
16
+ open(options[:pidfile] || "/var/run/popper.pid" , 'w') {|f| f << Process.pid}
17
+ else
18
+ Popper.init_logger(options, true)
19
+ end
20
+
11
21
  Popper.load_config(options)
12
- Popper.init_logger(options)
13
- Popper::Pop.run
22
+
23
+ accounts = Popper.configure.accounts.map {|account| MailAccount.new(account)}
24
+ while true
25
+ accounts.each(&:run)
26
+ sleep(60 || Popper.configure.global.interval)
27
+ end
28
+
14
29
  rescue => e
15
30
  Popper.log.fatal(e)
16
31
  Popper.log.fatal(e.backtrace)
17
32
  end
18
33
 
19
- desc "prepop", "get current mailbox all uidl"
20
- def prepop
34
+ class_option :config, type: :string, aliases: '-c'
35
+ desc "print", "print configure"
36
+ def print
21
37
  Popper.load_config(options)
22
- Popper.init_logger(options, true)
23
- Popper::Pop.prepop
38
+ Popper.configure.accounts.each do |account|
39
+ print_config(account)
40
+ end
24
41
  end
25
42
 
26
43
  desc "init", "create home dir"
@@ -36,5 +53,32 @@ module Popper
36
53
  def __print_version
37
54
  puts "Popper version:#{Popper::VERSION}"
38
55
  end
56
+
57
+ no_commands do
58
+ def print_config(config)
59
+ puts config.name
60
+ last_rule = nil
61
+ last_header = nil
62
+
63
+ config.rule_with_conditions_all? do |rule,mail_header,condition|
64
+ puts " "*1 + "rule[#{rule}]" if rule != last_rule
65
+ puts " "*2 + "actions" if rule != last_rule
66
+
67
+ config.action_by_rule(rule).each_pair do |action,params|
68
+ puts " "*3 + "#{action}"
69
+ params.each_pair do |k,v|
70
+ puts " "*4 + "#{k} #{v}"
71
+ end
72
+ end if rule != last_rule
73
+
74
+ puts " "*2 + "header[#{mail_header}]" if mail_header != last_header
75
+ puts " "*3 + "#{condition}"
76
+
77
+ last_rule = rule
78
+ last_header = mail_header
79
+ true
80
+ end
81
+ end
82
+ end
39
83
  end
40
84
  end
@@ -3,15 +3,16 @@ require 'ostruct'
3
3
  require 'logger'
4
4
  module Popper
5
5
  class Config
6
- attr_reader :default, :accounts
6
+ attr_reader :global, :default, :accounts
7
7
  def initialize(config_path)
8
8
  raise "configure not fond #{config_path}" unless File.exist?(config_path)
9
9
 
10
10
  config = TOML.load_file(config_path)
11
+ @global = AccountAttributes.new(config["global"]) if config["global"]
11
12
  @default = AccountAttributes.new(config["default"]) if config["default"]
12
13
  @accounts = []
13
14
 
14
- config.select {|k,v| !%w(default).include?(k) }.each do |account|
15
+ config.select {|k,v| !%w(default global).include?(k) }.each do |account|
15
16
  _account = AccountAttributes.new(account[1])
16
17
  _account.name = account[0]
17
18
  @accounts << _account
@@ -66,11 +67,17 @@ module Popper
66
67
  end
67
68
  end
68
69
 
69
-
70
+ def rule_with_conditions_all?(&block)
71
+ rules.to_h.keys.find do |rule|
72
+ condition_by_rule(rule).to_h.all? do |mail_header,conditions|
73
+ block.call(rule, mail_header, conditions)
74
+ end
75
+ end
76
+ end
70
77
  end
71
78
 
72
79
  def self.load_config(options)
73
- config_path = options[:config] || File.join(Dir.home, "popper", "popper.conf")
80
+ config_path = options[:config] || "/etc/popper.conf"
74
81
  @_config = Config.new(config_path)
75
82
  end
76
83
 
@@ -1,18 +1,20 @@
1
1
  module Popper
2
2
  class Init
3
3
  def self.run(options)
4
- dirname = options[:config] || File.join(Dir.home, "popper")
5
- unless FileTest.exist?(dirname)
6
- FileUtils.mkdir_p(dirname)
7
- open("#{dirname}/popper.conf","w") do |e|
4
+ filename = options[:config] || "/etc/popper.conf"
5
+ unless FileTest.exist?(filename)
6
+ open(filename,"w") do |e|
8
7
  e.puts sample_config
9
- end if FileTest.exist?(dirname)
10
- puts "create directry ~/popper"
8
+ end
9
+ puts "create sample config #{filename}"
11
10
  end
12
11
  end
13
12
 
14
13
  def self.sample_config
15
14
  <<-EOS
15
+ [global]
16
+ interval = 60 # fetch interbal default:60
17
+
16
18
  [default.condition]
17
19
  subject = ["^(?!.*Re:).+$"]
18
20
 
@@ -0,0 +1,100 @@
1
+ require 'net/pop'
2
+ require 'mail'
3
+ require 'kconv'
4
+
5
+ module Popper
6
+ class MailAccount
7
+ def initialize(config)
8
+ @config = config
9
+ end
10
+
11
+ def run
12
+ session_start do |conn|
13
+ @current_uidl_list = conn.mails.map(&:uidl)
14
+ @complete_uidl_list = @current_uidl_list unless @complete_uidl_list
15
+ pop(conn)
16
+ end
17
+ rescue => e
18
+ Popper.log.warn e
19
+ end
20
+
21
+ def pop(conn)
22
+ done_uidls = []
23
+ error_uidls = []
24
+
25
+ Popper.log.info "start popper #{@config.name}"
26
+
27
+ process_uidl_list(conn).each do |m|
28
+ begin
29
+ mail = EncodeMail.new(m.mail)
30
+ Popper.log.info "check mail:#{mail.date.to_s} #{mail.subject}"
31
+
32
+ if rule = match_rule?(mail)
33
+ Popper.log.info "do action:#{mail.subject}"
34
+ Popper::Action::Git.run(@config.action_by_rule(rule), mail) if @config.action_by_rule(rule)
35
+ end
36
+ done_uidls << m.uidl
37
+
38
+ rescue Net::POPError => e
39
+ @complete_uidl_list += done_uidls
40
+ Popper.log.warn "pop err write uidl"
41
+ return
42
+ rescue => e
43
+ error_uidls << m.uidl
44
+ Popper.log.warn e
45
+ end
46
+ end
47
+
48
+ @complete_uidl_list = @current_uidl_list - error_uidls
49
+ Popper.log.info "success popper #{@config.name}"
50
+ end
51
+
52
+ def session_start(&block)
53
+ pop = Net::POP3.new(@config.login.server, @config.login.port || 110)
54
+ pop.open_timeout = ENV['POP_TIMEOUT'] || 120
55
+ pop.read_timeout = ENV['POP_TIMEOUT'] || 120
56
+ pop.start(
57
+ @config.login.user,
58
+ @config.login.password
59
+ ) do |pop|
60
+ Popper.log.info "connect server #{@config.name}"
61
+ block.call(pop)
62
+ Popper.log.info "disconnect server #{@config.name}"
63
+ end
64
+ end
65
+
66
+ def process_uidl_list(conn)
67
+ uidl_list = @current_uidl_list - @complete_uidl_list
68
+ conn.mails.select {|_m|uidl_list.include?(_m.uidl)}
69
+ end
70
+
71
+ def match_rule?(mail)
72
+ @config.rule_with_conditions_all? do |rule, mail_header, conditions|
73
+ conditions.all? do |condition|
74
+ mail.respond_to?(mail_header) && mail.send(mail_header).to_s.match(/#{condition}/)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ class ::Hash
82
+ def deep_merge(second)
83
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
84
+ self.merge(second.to_h, &merger)
85
+ end
86
+
87
+ def deep_merge!(second)
88
+ self.merge!(deep_merge(second))
89
+ end
90
+ end
91
+
92
+ class EncodeMail < Mail::Message
93
+ def subject
94
+ Kconv.toutf8(self[:Subject].value) if self[:Subject]
95
+ end
96
+
97
+ def body
98
+ super.decoded.encode("UTF-8", self.charset)
99
+ end
100
+ end
@@ -1,3 +1,3 @@
1
1
  module Popper
2
- VERSION = "0.1.8"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: popper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pyama86
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-09-15 00:00:00.000000000 Z
11
+ date: 2015-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -139,6 +139,7 @@ files:
139
139
  - bin/console
140
140
  - bin/setup
141
141
  - exe/popper
142
+ - init_script/cent7/etc/systemd/system/popper.service
142
143
  - lib/popper.rb
143
144
  - lib/popper/action.rb
144
145
  - lib/popper/action/base.rb
@@ -148,8 +149,7 @@ files:
148
149
  - lib/popper/cli.rb
149
150
  - lib/popper/config.rb
150
151
  - lib/popper/init.rb
151
- - lib/popper/pop.rb
152
- - lib/popper/sync.rb
152
+ - lib/popper/mail_account.rb
153
153
  - lib/popper/version.rb
154
154
  - popper.gemspec
155
155
  homepage: http://ten-snapon.com
@@ -1,125 +0,0 @@
1
- require 'net/pop'
2
- require 'mail'
3
- require 'kconv'
4
- module Popper
5
- class Pop
6
- def self.run
7
- begin
8
- Popper::Sync.synchronized do
9
- Popper.configure.accounts.each do |account|
10
- begin
11
- pop(account)
12
- rescue => e
13
- Popper.log.warn e
14
- end
15
- end
16
- end
17
- rescue Locked
18
- puts "There will be a running process"
19
- end
20
- end
21
-
22
- def self.pop(account)
23
- done_uidls = []
24
- error_uidls = []
25
- Popper.log.info "start popper #{account.name}"
26
- connection(account) do |pop|
27
- current_uidls = pop.mails.map(&:uidl)
28
- target_uidls = current_uidls - last_uidl(account.name)
29
- pop.mails.select {|_m|target_uidls.include?(_m.uidl) }.each do |m|
30
- begin
31
- mail = EncodeMail.new(m.mail)
32
- Popper.log.info "check mail:#{mail.date.to_s} #{mail.subject}"
33
- if rule = matching?(account, mail)
34
- Popper.log.info "do action:#{mail.subject}"
35
- Popper::Action::Git.run(account.action_by_rule(rule), mail) if account.action_by_rule(rule)
36
- end
37
- done_uidls << m.uidl
38
- rescue Net::POPError => e
39
- last_uidl(account.name, last_uidl(account.name) + done_uidls)
40
- raise e
41
- rescue => e
42
- error_uidls << m.uidl
43
- Popper.log.warn e
44
- end
45
- end
46
- # write cache
47
- last_uidl(account.name, current_uidls - error_uidls)
48
- Popper.log.info "success popper #{account.name}"
49
- end
50
- end
51
-
52
- def self.connection(account, &block)
53
- pop = Net::POP3.new(account.login.server, account.login.port || 110)
54
- pop.open_timeout = ENV['POP_TIMEOUT'] || 120
55
- pop.read_timeout = ENV['POP_TIMEOUT'] || 120
56
- pop.start(
57
- account.login.user,
58
- account.login.password
59
- ) do |pop|
60
- Popper.log.info "connect server #{account.name}"
61
- block.call(pop)
62
- Popper.log.info "disconnect server #{account.name}"
63
- end
64
- end
65
-
66
- def self.matching?(account, mail)
67
- account.rules.to_h.keys.find do |rule|
68
- account.condition_by_rule(rule).to_h.all? do |header,conditions|
69
- conditions.all? do |condition|
70
- mail.respond_to?(header) && mail.send(header).to_s.match(/#{condition}/)
71
- end
72
- end
73
- end
74
- end
75
-
76
- def self.last_uidl(account, uidl=nil)
77
- path = File.join(Dir.home, "popper", ".#{account}.uidl")
78
- @_uidl ||= {}
79
-
80
- File.write(File.join(path), uidl.join("\n")) if uidl
81
-
82
- @_uidl[account] ||= File.exist?(path) ? File.read(path).split(/\r?\n/) : []
83
- @_uidl[account]
84
- end
85
-
86
- def self.prepop
87
- Popper.configure.accounts.each do |account|
88
- Popper.log.info "start prepop #{account.name}"
89
- begin
90
- connection(account) do |pop|
91
- uidls = pop.mails.map(&:uidl)
92
- last_uidl(
93
- account.name,
94
- uidls
95
- )
96
- Popper.log.info "success prepop #{account.name} mail count:#{uidls.count}"
97
- end
98
- rescue => e
99
- Popper.log.warn e
100
- end
101
- end
102
- end
103
- end
104
- end
105
-
106
- class ::Hash
107
- def deep_merge(second)
108
- merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
109
- self.merge(second.to_h, &merger)
110
- end
111
-
112
- def deep_merge!(second)
113
- self.merge!(deep_merge(second))
114
- end
115
- end
116
-
117
- class EncodeMail < Mail::Message
118
- def subject
119
- Kconv.toutf8(self[:Subject].value) if self[:Subject]
120
- end
121
-
122
- def body
123
- super.decoded.encode("UTF-8", self.charset)
124
- end
125
- end
@@ -1,18 +0,0 @@
1
- module Popper
2
- class Locked < StandardError; end
3
- class Sync
4
- def self.synchronized
5
- File.open(lockfile, 'w') do |_lockfile|
6
- if _lockfile.flock(File::LOCK_EX|File::LOCK_NB)
7
- yield
8
- else
9
- raise Locked
10
- end
11
- end
12
- end
13
-
14
- def self.lockfile
15
- File.join(Dir.home, "popper", "popper.lock")
16
- end
17
- end
18
- end