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.
- data/Gemfile +23 -0
- data/README.md +109 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/app.rb +15 -0
- data/automatic.gemspec +122 -0
- data/bin/automatic +15 -0
- data/config/default.yml +29 -0
- data/config/feed2console.yml +15 -0
- data/db/.gitkeep +0 -0
- data/doc/COPYING +674 -0
- data/doc/ChangeLog +17 -0
- data/doc/PLUGINS.ja +205 -0
- data/doc/README.ja +449 -0
- data/lib/automatic.rb +60 -0
- data/lib/automatic/environment.rb +8 -0
- data/lib/automatic/feed_parser.rb +36 -0
- data/lib/automatic/log.rb +24 -0
- data/lib/automatic/pipeline.rb +36 -0
- data/lib/automatic/recipe.rb +31 -0
- data/lib/config/validator.rb +83 -0
- data/plugins/custom_feed/svn_log.rb +56 -0
- data/plugins/filter/ignore.rb +60 -0
- data/plugins/filter/image.rb +47 -0
- data/plugins/filter/tumblr_resize.rb +40 -0
- data/plugins/notify/ikachan.rb +85 -0
- data/plugins/publish/console.rb +31 -0
- data/plugins/publish/google_calendar.rb +86 -0
- data/plugins/publish/hatena_bookmark.rb +103 -0
- data/plugins/store/full_text.rb +57 -0
- data/plugins/store/permalink.rb +47 -0
- data/plugins/store/store_database.rb +53 -0
- data/plugins/store/target_link.rb +47 -0
- data/plugins/subscription/feed.rb +30 -0
- data/script/bootstrap +117 -0
- data/spec/plugins/custom_feed/svn_log_spec.rb +31 -0
- data/spec/plugins/filter/ignore_spec.rb +37 -0
- data/spec/plugins/filter/image_spec.rb +55 -0
- data/spec/plugins/filter/tumblr_resize_spec.rb +102 -0
- data/spec/plugins/notify/ikachan_spec.rb +28 -0
- data/spec/plugins/publish/console_spec.rb +24 -0
- data/spec/plugins/publish/google_calendar_spec.rb +82 -0
- data/spec/plugins/publish/hatena_bookmark_spec.rb +36 -0
- data/spec/plugins/store/full_text_spec.rb +39 -0
- data/spec/plugins/store/permalink_spec.rb +39 -0
- data/spec/plugins/store/target_link_spec.rb +30 -0
- data/spec/plugins/subscription/feed_spec.rb +36 -0
- data/spec/spec_helper.rb +82 -0
- data/test/integration/test_activerecord.yml +24 -0
- data/test/integration/test_fulltext.yml +24 -0
- data/test/integration/test_hatenabookmark.yml +26 -0
- data/test/integration/test_ignore.yml +22 -0
- data/test/integration/test_ignore2.yml +25 -0
- data/test/integration/test_image2local.yml +26 -0
- data/test/integration/test_svnlog.yml +14 -0
- data/test/integration/test_tumblr2local.yml +26 -0
- data/utils/auto_discovery.rb +18 -0
- data/utils/opml_parser.rb +247 -0
- data/vendor/.gitkeep +0 -0
- 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
|