automatic 12.2.1

Sign up to get free protection for your applications and to get access to all the features.
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