popper 0.1.8 → 0.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.
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