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