news2kindle 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7d94ebc86c80c6feb22ad91f1467083a2d0ef3c7
4
+ data.tar.gz: 65e21c3bf9c757f41a030268b6e413370c0ccc16
5
+ SHA512:
6
+ metadata.gz: 28ee0b8f40be33d8cbd401e13c4d5e03a628798056e069b7a539bff484f48483d6a052a088eff9ea1b55635fd0cd0427ff729540cc7eb85d9cf7150abefabcf3
7
+ data.tar.gz: f21b50fc0f883cf0d8eba883651e0ffb5a8680b905e5ce57bf0f546764cdfc39a885f6038634760b555003d43d4bf0b79af85af11959aff2155d29848eb6f09e
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ .bundle
2
+ .env
3
+ .rspec_status
4
+ .ruby-version
5
+ /vendor/bundle
6
+ nikkei-paid
7
+ nikkei-free
8
+ internet-watch
9
+ /.bundle/
10
+ /pkg/
11
+ /spec/reports/
12
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.tachikoma.yml ADDED
@@ -0,0 +1 @@
1
+ strategy: 'bundler'
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ sudo: false
2
+ cache: bundler
3
+
4
+ before_install:
5
+ - gem install bundler -v 1.16.0.pre.3
6
+
7
+ rvm:
8
+ - 2.3.5
9
+ - 2.4.2
10
+
11
+ script: bundle exec rake
12
+
13
+ branches:
14
+ only:
15
+ - master
16
+
17
+ services:
18
+ - mongodb
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in news2kindle.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,119 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ news2kindle (0.1.1)
5
+ dropbox_api
6
+ kindlegen
7
+ mail
8
+ mechanize
9
+ mongoid (~> 6.1)
10
+ nokogiri
11
+ pit
12
+ systemu
13
+
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ activemodel (5.1.4)
18
+ activesupport (= 5.1.4)
19
+ activesupport (5.1.4)
20
+ concurrent-ruby (~> 1.0, >= 1.0.2)
21
+ i18n (~> 0.7)
22
+ minitest (~> 5.1)
23
+ tzinfo (~> 1.1)
24
+ bson (4.2.2)
25
+ coderay (1.1.2)
26
+ concurrent-ruby (1.0.5)
27
+ diff-lcs (1.3)
28
+ domain_name (0.5.20170404)
29
+ unf (>= 0.0.5, < 1.0.0)
30
+ dropbox_api (0.1.10)
31
+ faraday (~> 0.9, ~> 0.8)
32
+ oauth2 (~> 1.1)
33
+ faraday (0.12.2)
34
+ multipart-post (>= 1.2, < 3)
35
+ http-cookie (1.0.3)
36
+ domain_name (~> 0.5)
37
+ i18n (0.9.0)
38
+ concurrent-ruby (~> 1.0)
39
+ jwt (1.5.6)
40
+ kindlegen (3.0.3)
41
+ rake
42
+ rubyzip
43
+ mail (2.6.6)
44
+ mime-types (>= 1.16, < 4)
45
+ mechanize (2.7.5)
46
+ domain_name (~> 0.5, >= 0.5.1)
47
+ http-cookie (~> 1.0)
48
+ mime-types (>= 1.17.2)
49
+ net-http-digest_auth (~> 1.1, >= 1.1.1)
50
+ net-http-persistent (~> 2.5, >= 2.5.2)
51
+ nokogiri (~> 1.6)
52
+ ntlm-http (~> 0.1, >= 0.1.1)
53
+ webrobots (>= 0.0.9, < 0.2)
54
+ method_source (0.9.0)
55
+ mime-types (3.1)
56
+ mime-types-data (~> 3.2015)
57
+ mime-types-data (3.2016.0521)
58
+ mini_portile2 (2.3.0)
59
+ minitest (5.10.3)
60
+ mongo (2.4.3)
61
+ bson (>= 4.2.1, < 5.0.0)
62
+ mongoid (6.2.1)
63
+ activemodel (~> 5.1)
64
+ mongo (>= 2.4.1, < 3.0.0)
65
+ multi_json (1.12.2)
66
+ multi_xml (0.6.0)
67
+ multipart-post (2.0.0)
68
+ net-http-digest_auth (1.4.1)
69
+ net-http-persistent (2.9.4)
70
+ nokogiri (1.8.1)
71
+ mini_portile2 (~> 2.3.0)
72
+ ntlm-http (0.1.1)
73
+ oauth2 (1.4.0)
74
+ faraday (>= 0.8, < 0.13)
75
+ jwt (~> 1.0)
76
+ multi_json (~> 1.3)
77
+ multi_xml (~> 0.5)
78
+ rack (>= 1.2, < 3)
79
+ pit (0.0.7)
80
+ pry (0.11.1)
81
+ coderay (~> 1.1.0)
82
+ method_source (~> 0.9.0)
83
+ rack (2.0.3)
84
+ rake (12.1.0)
85
+ rspec (3.6.0)
86
+ rspec-core (~> 3.6.0)
87
+ rspec-expectations (~> 3.6.0)
88
+ rspec-mocks (~> 3.6.0)
89
+ rspec-core (3.6.0)
90
+ rspec-support (~> 3.6.0)
91
+ rspec-expectations (3.6.0)
92
+ diff-lcs (>= 1.2.0, < 2.0)
93
+ rspec-support (~> 3.6.0)
94
+ rspec-mocks (3.6.0)
95
+ diff-lcs (>= 1.2.0, < 2.0)
96
+ rspec-support (~> 3.6.0)
97
+ rspec-support (3.6.0)
98
+ rubyzip (1.2.1)
99
+ systemu (2.6.5)
100
+ thread_safe (0.3.6)
101
+ tzinfo (1.2.3)
102
+ thread_safe (~> 0.1)
103
+ unf (0.1.4)
104
+ unf_ext
105
+ unf_ext (0.0.7.4)
106
+ webrobots (0.1.2)
107
+
108
+ PLATFORMS
109
+ ruby
110
+
111
+ DEPENDENCIES
112
+ bundler
113
+ news2kindle!
114
+ pry
115
+ rake
116
+ rspec
117
+
118
+ BUNDLED WITH
119
+ 1.16.0.pre.3
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # News2Kindle
2
+ ニュースサイトを定期的にスクレイピングしてmobiファイルを生成し、Kindle Personal Documentへメールで送信するコマンドライン・ツール
3
+
4
+ ニュースの電子書籍化と配信を自動化するソフトウェアとしては電子書籍管理ツールであるCalibreが豊富なレシピで抜きん出た存在ですが、クライアントPCを常時稼動しておかなくてはならず、環境面で稼働が難しい面があります。そこで、サーバ上でcronタスクとして稼働する仕組みを作りました。ただしレシピはまだぜんぜんありません(作者が使っている日経新聞電子版とINTERNET Watch、tDiaryのみ)。
5
+
6
+ ## 仕組み
7
+ cronタスクとして動かすことを前提にしています。
8
+
9
+ 実際にどのサイトをmobiファイル化するのかという指定は、`~/.news2kindle`ないし`./news2kindle.yaml`のconfigファイルで指定します。configファイルのサンプルです:
10
+
11
+ ```yaml
12
+ :tasks:
13
+ sites1:
14
+ :media:
15
+ - foo
16
+ - bar
17
+ :receiver:
18
+ - receiver1@example.com
19
+ sites2:
20
+ :media:
21
+ - buz
22
+ :receiver:
23
+ - dropbox:/Public
24
+ :sender: hoge@example.com
25
+ :email:
26
+ :address: smtp.sendgrid.net
27
+ :port: 587
28
+ :user_name: yes
29
+ :password: yes
30
+ :authentication: :plain
31
+ :mongodb_uri: mongodb://localhost:27017/news2kindle
32
+ ```
33
+
34
+ cronタスクを動かす時間によって、異なるニュースサイトにアクセスしたり、送り先を変えたいでしょう。そのため、コマンドに指定する「タスク(:tasks)」を分けて、それぞれに「メディア(:media)」と「送り先(:receiver)」を指定できるようになっています。receiverにはメールアドレスの他に「dropbox:」で始まるDropboxのディレクトリも指定できます。Dropboxを利用する場合には、Dropboxの開発者向けサービスから各種APIトークンを取得しておく必要があります。
35
+
36
+ 「送信元(:sender)」は、KPDサービスに登録してあるメールアドレスを指定します。
37
+
38
+ 「:email」は、メールサーバ(SMTP)の設定です。プログラムを動かしている環境からアクセスできるメールサーバの情報を指定します。なお、メールサーバが認証を必要とする場合は「:user_name」や「:password」を指定する必要がありますが、configファイルには直接書くことはありません。これらの項目を「yes」にしておくと、初回実行時にプログラムが聞いてきます(~/.pit/default.yamlというファイルに保存されます)。
39
+
40
+ 「:mongodb_uri」はURIの重複チェックをする場合にMongoDBの情報を指定します。日に何度も動かすと、すでに取得済みの記事が重複して含まれてしまいますが、それをチェックして除外したい場合に利用します。URIにパスワード等の情報が含まれている場合には「yes」とだけ指定しておくと、最初の実行時に尋ねてくるようになります(Pitによって安全な別ファイルに保管されます)。
41
+
42
+ mobiファイルの生成に成功すると、指定したアドレスにメールを送ったり、Dropboxの指定フォルダに保存します。メールアドレスは、実際は〜@kindle.comになるでしょう(:receiver)。また、送信元のアドレスもKindle Personal Documentで許可したアドレスを指定して置く必要があります(:sender)。
43
+
44
+ ## 動かし方
45
+ インストール方法:
46
+
47
+ ```sh
48
+ % gem install news2kindle
49
+ ```
50
+
51
+ news2kindleコマンドを実行すると、ヘルプが出ます。適切なconfigファイルがあれば、そこに記述してあるタスクを指定することで、mobiファイルが生成され、指定した送信先へ送られます。
52
+
53
+
54
+ ## ジェネレータの作り方
55
+ あとで書く。
56
+
57
+ ## Contributing
58
+
59
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tdtds/news2kindle.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "news2kindle"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ require 'news2kindle'
3
+ require 'kindlegen'
4
+ require 'logger'
5
+
6
+ News2Kindle::DupChecker.setup({
7
+ clients: {
8
+ default: {
9
+ uri: 'mongodb://localhost:27017/news2kindle'
10
+ }
11
+ }
12
+ })
13
+
14
+ ARGV.each do |task|
15
+ require "news2kindle/generator/#{task}"
16
+ gen = News2Kindle::Generator.const_get(task.split(/-/).map{|a|a.capitalize}.join)
17
+ Dir::mkdir(task)
18
+ gen.new(task).generate({now:Time::now}) do |opf|
19
+ Kindlegen.run(opf, '-o', "#{task}.mobi")
20
+ end
21
+ end
data/exe/news2kindle ADDED
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # news2kindle: scraping news sites and generate kindle document
4
+ #
5
+ # Copyright (C) 2017 by TADA Tadashi <t@tdtds.jp>
6
+ # Distributed under GPL
7
+ #
8
+
9
+ require 'news2kindle'
10
+ require 'optparse'
11
+ require 'open-uri'
12
+ require 'yaml'
13
+
14
+ module News2Kindle
15
+
16
+ class CLI
17
+ def run
18
+ conf, tasks = parse_options
19
+ News2Kindle.logger.level = Logger::DEBUG if conf[:verbose]
20
+
21
+ if conf[:mongodb_uri]
22
+ unless conf[:mongodb_uri] =~ %r|^mongodb://|
23
+ conf[:mongodb_uri] = Pit::get('news2kindle', require: {
24
+ mongodb_uri: 'MongoDB URI for Dupulicate Check starts with "mongodb://".'
25
+ })[:mongodb_uri]
26
+ end
27
+ if conf[:mongodb_uri]
28
+ DupChecker.setup({clients:{default:{uri:conf[:mongodb_uri]}}})
29
+ end
30
+ end
31
+
32
+ tasks.each do |name|
33
+ task = conf[:tasks][name]
34
+ usage("task '#{name}' not found") unless task
35
+
36
+ task[:media].each do |media|
37
+ begin
38
+ opts = {now: Time.now}.merge(task[:option] || {})
39
+ News2Kindle.logger.info "starting #{media}..."
40
+ Task::new(media).run(task[:receiver], conf[:sender], opts)
41
+ rescue
42
+ News2Kindle.logger.fatal($!)
43
+ raise
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+ def parse_options(argv = ARGV)
51
+ op = OptionParser.new
52
+
53
+ self.class.module_eval do
54
+ define_method(:usage) do |msg = nil|
55
+ puts op.to_s
56
+ puts "error: #{msg}" if msg
57
+ exit 1
58
+ end
59
+ end
60
+
61
+ opts = {
62
+ verbose: false
63
+ }
64
+
65
+ op.on('-C', '--config VALUE', "configuration file") do |v|
66
+ usage("#{v} is not existent") unless Pathname(v).expand_path.exist?
67
+ opts[:config] = v
68
+ end
69
+ op.on('-s', '--sender VALUE', "sender e-mail address") do |v|
70
+ opts[:sender] = v
71
+ end
72
+ op.on('-m', '--mongodb-uri VALUE', "MongoDB URI for dupulicate check or 'no'") do |v|
73
+ opts[:mongodb_uri] = v == 'no' ? false : v
74
+ end
75
+ op.on('-V', '--verbose', "print verbose messages") do |v|
76
+ opts[:verbose] = true
77
+ end
78
+
79
+ op.banner += ' TASK1 [TASK2...]'
80
+ begin
81
+ args = op.parse(argv)
82
+ rescue OptionParser::InvalidOption => e
83
+ usage e.message
84
+ end
85
+
86
+ conf = nil
87
+ [opts[:config], './news2kindle.yaml', '~/.news2kindle'].each do |config_file|
88
+ begin
89
+ file = Pathname(config_file).expand_path
90
+ conf = YAML.load_file(file)
91
+ opts[:config] = file
92
+ break
93
+ rescue TypeError, Errno::ENOENT
94
+ end
95
+ end
96
+ usage 'needs configuration file ./news2kindle.yaml or ~/.news2kindle' unless conf
97
+ conf.merge!(opts)
98
+
99
+ if args.size < 1
100
+ usage "needs some tasks: #{conf[:tasks].keys.join(', ')}"
101
+ end
102
+ [conf, args]
103
+ end
104
+ end
105
+ end
106
+
107
+ News2Kindle::CLI.new.run
@@ -0,0 +1,12 @@
1
+ require 'news2kindle/version'
2
+ require 'news2kindle/task'
3
+ require 'news2kindle/dup_checker'
4
+ require 'logger'
5
+
6
+ module News2Kindle
7
+ @logger = Logger.new(STDERR)
8
+ @logger.level = Logger::ERROR
9
+ @logger.formatter = proc{|severity, _, _, msg| "#{severity}: #{msg}\n"}
10
+ def self.logger; @logger; end
11
+ def self.logger=(logger); @logger = logger; end
12
+ end
@@ -0,0 +1,41 @@
1
+ # uri duplication checker
2
+ #
3
+ # Copyright (C) 2017 by TADA Tadashi <t@tdtds.jp>
4
+ # Distributed under GPL.
5
+ #
6
+ require 'mongoid'
7
+
8
+ module News2Kindle
9
+ class DupChecker
10
+ Mongo::Logger.level = Logger::WARN
11
+ @@mongoid_conf = nil
12
+
13
+ include Mongoid::Document
14
+ include Mongoid::Timestamps
15
+ store_in collection: 'uri'
16
+ field :uri, type: String
17
+
18
+ def self.setup(mongoid_conf)
19
+ @@mongoid_conf = mongoid_conf
20
+ end
21
+
22
+ def self.dup?(uri)
23
+ return false unless @@mongoid_conf
24
+ Mongoid::Config.load_configuration(@@mongoid_conf) if Mongoid::Config.clients.size == 0
25
+
26
+ begin
27
+ url = uri.to_s
28
+ if self.where(uri: uri.to_s).size == 0
29
+ self.create(uri: uri.to_s)
30
+ return false
31
+ else
32
+ return true
33
+ end
34
+ rescue Moped::Errors::ConnectionFailure
35
+ News2Kindle.logger.error $!
36
+ @@mongoid_conf = nil
37
+ return false
38
+ end
39
+ end
40
+ end
41
+ end