automatic 12.2.1

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.
Files changed (60) hide show
  1. data/Gemfile +23 -0
  2. data/README.md +109 -0
  3. data/Rakefile +42 -0
  4. data/VERSION +1 -0
  5. data/app.rb +15 -0
  6. data/automatic.gemspec +122 -0
  7. data/bin/automatic +15 -0
  8. data/config/default.yml +29 -0
  9. data/config/feed2console.yml +15 -0
  10. data/db/.gitkeep +0 -0
  11. data/doc/COPYING +674 -0
  12. data/doc/ChangeLog +17 -0
  13. data/doc/PLUGINS.ja +205 -0
  14. data/doc/README.ja +449 -0
  15. data/lib/automatic.rb +60 -0
  16. data/lib/automatic/environment.rb +8 -0
  17. data/lib/automatic/feed_parser.rb +36 -0
  18. data/lib/automatic/log.rb +24 -0
  19. data/lib/automatic/pipeline.rb +36 -0
  20. data/lib/automatic/recipe.rb +31 -0
  21. data/lib/config/validator.rb +83 -0
  22. data/plugins/custom_feed/svn_log.rb +56 -0
  23. data/plugins/filter/ignore.rb +60 -0
  24. data/plugins/filter/image.rb +47 -0
  25. data/plugins/filter/tumblr_resize.rb +40 -0
  26. data/plugins/notify/ikachan.rb +85 -0
  27. data/plugins/publish/console.rb +31 -0
  28. data/plugins/publish/google_calendar.rb +86 -0
  29. data/plugins/publish/hatena_bookmark.rb +103 -0
  30. data/plugins/store/full_text.rb +57 -0
  31. data/plugins/store/permalink.rb +47 -0
  32. data/plugins/store/store_database.rb +53 -0
  33. data/plugins/store/target_link.rb +47 -0
  34. data/plugins/subscription/feed.rb +30 -0
  35. data/script/bootstrap +117 -0
  36. data/spec/plugins/custom_feed/svn_log_spec.rb +31 -0
  37. data/spec/plugins/filter/ignore_spec.rb +37 -0
  38. data/spec/plugins/filter/image_spec.rb +55 -0
  39. data/spec/plugins/filter/tumblr_resize_spec.rb +102 -0
  40. data/spec/plugins/notify/ikachan_spec.rb +28 -0
  41. data/spec/plugins/publish/console_spec.rb +24 -0
  42. data/spec/plugins/publish/google_calendar_spec.rb +82 -0
  43. data/spec/plugins/publish/hatena_bookmark_spec.rb +36 -0
  44. data/spec/plugins/store/full_text_spec.rb +39 -0
  45. data/spec/plugins/store/permalink_spec.rb +39 -0
  46. data/spec/plugins/store/target_link_spec.rb +30 -0
  47. data/spec/plugins/subscription/feed_spec.rb +36 -0
  48. data/spec/spec_helper.rb +82 -0
  49. data/test/integration/test_activerecord.yml +24 -0
  50. data/test/integration/test_fulltext.yml +24 -0
  51. data/test/integration/test_hatenabookmark.yml +26 -0
  52. data/test/integration/test_ignore.yml +22 -0
  53. data/test/integration/test_ignore2.yml +25 -0
  54. data/test/integration/test_image2local.yml +26 -0
  55. data/test/integration/test_svnlog.yml +14 -0
  56. data/test/integration/test_tumblr2local.yml +26 -0
  57. data/utils/auto_discovery.rb +18 -0
  58. data/utils/opml_parser.rb +247 -0
  59. data/vendor/.gitkeep +0 -0
  60. metadata +205 -0
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::Filter::TumblrResize
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 28, 2012
6
+ # Updated:: Feb 28, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+
10
+ module Automatic::Plugin
11
+ class FilterTumblrResize
12
+
13
+ def initialize(config, pipeline=[])
14
+ @config = config
15
+ @pipeline = pipeline
16
+ end
17
+
18
+ def resize(string)
19
+ string = string.gsub("_75sq\.", "_1280\.")
20
+ string = string.gsub("_100\.", "_1280\.")
21
+ string = string.gsub("_250\.", "_1280\.")
22
+ string = string.gsub("_400\.", "_1280\.")
23
+ string = string.gsub("_500\.", "_1280\.")
24
+ end
25
+
26
+ def run
27
+ return_feeds = []
28
+ @pipeline.each {|feeds|
29
+ img_url = ""
30
+ unless feeds.nil?
31
+ feeds.items.each {|feed|
32
+ feed.link = resize(feed.link) unless feed.link.nil?
33
+ }
34
+ end
35
+ return_feeds << feeds
36
+ }
37
+ return_feeds
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'active_support/core_ext'
5
+
6
+ module Automatic::Plugin
7
+ class Ikachan
8
+ require 'rubygems'
9
+ require 'time'
10
+ require 'net/http'
11
+ require 'uri'
12
+
13
+ attr_accessor :params
14
+
15
+ def initialize
16
+ @params = {
17
+ "url" => "",
18
+ "port" => "",
19
+ "channels" => [],
20
+ "command" => "",
21
+ }
22
+ end
23
+
24
+ def endpoint_url
25
+ "#{@params['url']}:#{@params['port']}/#{@params['command']}"
26
+ end
27
+
28
+ def post(link, title = "")
29
+ message = build_message(link, title)
30
+ uri = URI.parse(endpoint_url)
31
+ proxy_class = Net::HTTP::Proxy(ENV["PROXY"], 8080)
32
+ http = proxy_class.new(uri.host, uri.port)
33
+ http.start do |http|
34
+ @params['channels'].each do|channel|
35
+ http.post("/join", "channel=#{channel}") # send join command to make sure when if ikachan is not in the channel
36
+ res = http.post(uri.path, %Q(channel=#{channel}&message=#{message}))
37
+ t = Time.now.strftime("%Y/%m/%d %X")
38
+ if res.code == "200" then
39
+ puts "#{t} [info] Success: #{message}"
40
+ else
41
+ puts "#{t} [error] #{res.code} Error: #{message}"
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+ def build_message(link, title)
49
+ message = ""
50
+ message += "#{title.to_s} - " unless title.blank?
51
+ message += link.to_s
52
+ message
53
+ end
54
+ end
55
+
56
+ class NotifyIkachan
57
+ attr_accessor :ikachan
58
+
59
+ def initialize(config, pipeline=[])
60
+ @config = config
61
+ @pipeline = pipeline
62
+
63
+ @ikachan = Ikachan.new
64
+ @ikachan.params = {
65
+ "url" => @config['url'],
66
+ "port" => @config['port'] || "4979",
67
+ "channels" => @config['channels'].split(',').map{ |v| v.match(/^#/) ? v : "#" + v }, # Prifixing '#'
68
+ "command" => @config['command'] || "notice",
69
+ }
70
+ end
71
+
72
+ def run
73
+ @pipeline.each {|feeds|
74
+ unless feeds.nil?
75
+ feeds.items.each {|feed|
76
+ Automatic::Log.puts("info", %Q(Ikachan: [#{feed.link}] sending with params #{ikachan.params.to_s}...))
77
+ ikachan.post(feed.link, feed.title)
78
+ sleep @config['interval'].to_i
79
+ }
80
+ end
81
+ }
82
+ @pipeline
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::Publish::Console
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 23, 2012
6
+ # Updated:: Feb 24, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+
10
+ module Automatic::Plugin
11
+ class PublishConsole
12
+ require 'pp'
13
+
14
+ def initialize(config, pipeline=[])
15
+ @config = config
16
+ @pipeline = pipeline
17
+ @output = STDOUT
18
+ end
19
+
20
+ def run
21
+ @pipeline.each { |feeds|
22
+ unless feeds.nil?
23
+ feeds.items.each { |feed|
24
+ @output.puts("info", feed.pretty_inspect)
25
+ }
26
+ end
27
+ }
28
+ @pipeline
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::Publish::Googlecalendar
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 24, 2012
6
+ # Updated:: Feb 24, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+
10
+ module Automatic::Plugin
11
+ class Googlecalendar
12
+ attr_accessor :user, :feed
13
+
14
+ def initialize
15
+ @user = {
16
+ "username" => "",
17
+ "password" => ""
18
+ }
19
+ @feed = 'http://www.google.com/calendar/feeds/default/private/full'
20
+ end
21
+
22
+ def add(arg)
23
+ date = nil
24
+ time_st = nil
25
+ time_en = nil
26
+ text = ''
27
+
28
+ # Parse Date
29
+ require 'date'
30
+ if /^([0-9]+\/[0-9]+\/[0-9]+)\s*/ =~ arg
31
+ # yyyy/mm/dd
32
+ datestr = $1
33
+ text = $'
34
+ begin
35
+ date = Date.parse(datestr)
36
+ rescue ArgumentError
37
+ raise "不正な日付形式-1: [#{datestr}]"
38
+ end
39
+ end
40
+
41
+ puts "日付 : #{date}"
42
+ puts "タイトル : #{text}"
43
+
44
+ # Register to calendar
45
+ require 'rubygems'
46
+ require 'gcalapi'
47
+
48
+ cal = GoogleCalendar::Calendar.new(GoogleCalendar::Service.new(
49
+ @user["username"], @user["password"]), @feed)
50
+
51
+ event = cal.create_event
52
+ event.title = text
53
+ event.st = Time.mktime(date.year, date.month, date.day)
54
+ event.en = event.st
55
+ event.allday = true
56
+ event.save!
57
+ end
58
+ end
59
+
60
+ class PublishGoogleCalendar
61
+ attr_accessor :hb
62
+
63
+ def initialize(config, pipeline=[])
64
+ @config = config
65
+ @pipeline = pipeline
66
+
67
+ @gc = Googlecalendar.new
68
+ @gc.user = {
69
+ "hatena_id" => @config['username'],
70
+ "password" => @config['password']
71
+ }
72
+ end
73
+
74
+ def run
75
+ @pipeline.each {|feeds|
76
+ unless feeds.nil?
77
+ feeds.items.each {|feed|
78
+ @gc.add('今日 ' + feed.title)
79
+ sleep @config['interval'].to_i
80
+ }
81
+ end
82
+ }
83
+ @pipeline
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::Publish::HatenaBookmark
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 22, 2012
6
+ # Updated:: Feb 24, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+
10
+ module Automatic::Plugin
11
+ class HatenaBookmark
12
+ require 'rubygems'
13
+ require 'time'
14
+ require 'digest/sha1'
15
+ require 'net/http'
16
+ require 'uri'
17
+ #require 'nkf'
18
+
19
+ attr_accessor :user
20
+
21
+ def initialize
22
+ @user = {
23
+ "hatena_id" => "",
24
+ "password" => ""
25
+ }
26
+ end
27
+
28
+ def wsse(hatena_id, password)
29
+ # Unique value
30
+ nonce = [Time.now.to_i.to_s].pack('m').gsub(/\n/, '')
31
+ now = Time.now.utc.iso8601
32
+
33
+ # Base64 encoding for SHA1 Digested strings
34
+ digest = [Digest::SHA1.digest(nonce + now + password)].pack("m").gsub(/\n/, '')
35
+
36
+ {'X-WSSE' => sprintf(
37
+ %Q<UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s">,
38
+ hatena_id, digest, nonce, now)
39
+ }
40
+ end
41
+
42
+ def toXml(link, summary)
43
+ %Q(
44
+ <entry xmlns="http://purl.org/atom/ns#">
45
+ <title>dummy</title>
46
+ <link rel="related" type="text/html" href="#{link}" />
47
+ <summary type="text/plain">#{summary}</summary>
48
+ </entry>
49
+ )
50
+ end
51
+
52
+ def post(b_url, b_comment)
53
+ url = "http://b.hatena.ne.jp/atom/post"
54
+ header = wsse(@user["hatena_id"], @user["password"])
55
+ uri = URI.parse(url)
56
+ proxy_class = Net::HTTP::Proxy(ENV["PROXY"], 8080)
57
+ http = proxy_class.new(uri.host)
58
+ http.start {|http|
59
+ # b_url = NKF.nkf('-w', b_url)
60
+ # b_comment = NKF.nkf('-w', b_comment)
61
+ res = http.post(uri.path, toXml(b_url, b_comment), header)
62
+ t = Time.now.strftime("%Y/%m/%d %X")
63
+ if res.code == "201" then
64
+ unless b_comment.nil?
65
+ print "#{t} [info] Success: #{b_url} Comment: #{b_comment}\n"
66
+ else
67
+ print "#{t} [info] Success: #{b_url}\n"
68
+ end
69
+ else
70
+ print "#{t} [error] #{res.code} Error: #{b_url}\n"
71
+ end
72
+ }
73
+ end
74
+ end
75
+
76
+ class PublishHatenaBookmark
77
+ attr_accessor :hb
78
+
79
+ def initialize(config, pipeline=[])
80
+ @config = config
81
+ @pipeline = pipeline
82
+
83
+ @hb = HatenaBookmark.new
84
+ @hb.user = {
85
+ "hatena_id" => @config['username'],
86
+ "password" => @config['password']
87
+ }
88
+ end
89
+
90
+ def run
91
+ @pipeline.each {|feeds|
92
+ unless feeds.nil?
93
+ feeds.items.each {|feed|
94
+ Automatic::Log.puts("info", "Bookmarking: #{feed.link}")
95
+ hb.post(feed.link, nil)
96
+ sleep @config['interval'].to_i
97
+ }
98
+ end
99
+ }
100
+ @pipeline
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::Store::FullText
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 26, 2012
6
+ # Updated:: Feb 29, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+ require 'plugins/store/store_database'
10
+
11
+ module Automatic::Plugin
12
+ class Blog < ActiveRecord::Base
13
+ end
14
+
15
+ class StoreFullText
16
+ include Automatic::Plugin::StoreDatabase
17
+
18
+ def initialize(config, pipeline=[])
19
+ @config = config
20
+ @pipeline = pipeline
21
+ end
22
+
23
+ def column_definition
24
+ return {
25
+ :title => :string,
26
+ :link => :string,
27
+ :description => :string,
28
+ :content => :string,
29
+ :created_at => :string,
30
+ }
31
+ end
32
+
33
+ def unique_key
34
+ return :link
35
+ end
36
+
37
+ def model_class
38
+ return Automatic::Plugin::Blog
39
+ end
40
+
41
+ def run
42
+ return for_each_new_feed { |feed|
43
+ begin
44
+ Blog.create(
45
+ :title => feed.title,
46
+ :link => feed.link,
47
+ :description => feed.description,
48
+ :content => feed.content_encoded,
49
+ :created_at => Time.now.strftime("%Y/%m/%d %X"))
50
+ Automatic::Log.puts("info", "Saving: #{feed.link}")
51
+ rescue
52
+ Automatic::Log.puts("warn", "Skip feed due to fault in save.")
53
+ end
54
+ }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::Store::Permalink
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 22, 2012
6
+ # Updated:: Feb 29, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+ require 'plugins/store/store_database'
10
+
11
+ module Automatic::Plugin
12
+ class Permalink < ActiveRecord::Base
13
+ end
14
+
15
+ class StorePermalink
16
+ include Automatic::Plugin::StoreDatabase
17
+
18
+ def initialize(config, pipeline=[])
19
+ @config = config
20
+ @pipeline = pipeline
21
+ end
22
+
23
+ def column_definition
24
+ return {
25
+ :url => :string,
26
+ :created_at => :string
27
+ }
28
+ end
29
+
30
+ def unique_key
31
+ return :url
32
+ end
33
+
34
+ def model_class
35
+ return Automatic::Plugin::Permalink
36
+ end
37
+
38
+ def run
39
+ return for_each_new_feed { |feed|
40
+ Permalink.create(
41
+ :url => feed.link,
42
+ :created_at => Time.now.strftime("%Y/%m/%d %X"))
43
+ Automatic::Log.puts("info", "Saving: #{feed.link}")
44
+ }
45
+ end
46
+ end
47
+ end