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
data/lib/automatic.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Name:: Automatic::Ruby
|
3
|
+
# Author:: 774 <http://id774.net>
|
4
|
+
# Version:: 12.02.1-devel
|
5
|
+
# Created:: Feb 18, 2012
|
6
|
+
# Updated:: Mar 2, 2012
|
7
|
+
# Copyright:: 774 Copyright (c) 2012
|
8
|
+
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
9
|
+
|
10
|
+
module Automatic
|
11
|
+
require 'automatic/environment'
|
12
|
+
require 'automatic/recipe'
|
13
|
+
require 'automatic/pipeline'
|
14
|
+
require 'automatic/log'
|
15
|
+
require 'automatic/feed_parser'
|
16
|
+
|
17
|
+
VERSION = "12.02.1-devel"
|
18
|
+
|
19
|
+
def self.root_dir
|
20
|
+
@root_dir
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.plugins_dir
|
24
|
+
@root_dir + "/plugins/"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.config_dir
|
28
|
+
@root_dir + "/config/"
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.run(root_dir)
|
32
|
+
@root_dir = root_dir
|
33
|
+
recipe_path = ""
|
34
|
+
require 'optparse'
|
35
|
+
parser = OptionParser.new { |parser|
|
36
|
+
parser.banner = "Usage: automatic [options] arg"
|
37
|
+
parser.version = VERSION
|
38
|
+
parser.separator "options:"
|
39
|
+
parser.on('-c', '--config FILE', String,
|
40
|
+
"recipe YAML file"){|c| recipe_path = c}
|
41
|
+
parser.on('-h', '--help', "show this message") {
|
42
|
+
puts parser
|
43
|
+
exit
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
begin
|
48
|
+
parser.parse!
|
49
|
+
print "Loading #{recipe_path}\n" unless recipe_path == ""
|
50
|
+
rescue OptionParser::ParseError => err
|
51
|
+
$stderr.puts err.message
|
52
|
+
$stderr.puts parser.help
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
|
56
|
+
# recipe treat as an object.
|
57
|
+
recipe = Automatic::Recipe.new(recipe_path)
|
58
|
+
Automatic::Pipeline.run(recipe)
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
# Set up gems listed in the Gemfile.
|
5
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
6
|
+
|
7
|
+
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
8
|
+
Bundler.require :default if defined?(Bundler)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# Name:: Automatic::FeedParser
|
4
|
+
# Author:: 774 <http://id774.net>
|
5
|
+
# Created:: Feb 19, 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
|
11
|
+
module FeedParser
|
12
|
+
require 'rss'
|
13
|
+
require 'uri'
|
14
|
+
|
15
|
+
def self.get_rss(url)
|
16
|
+
begin
|
17
|
+
unless url.nil?
|
18
|
+
feed = URI.parse(url).normalize
|
19
|
+
open(feed) { |http|
|
20
|
+
response = http.read
|
21
|
+
RSS::Parser.parse(response, false)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
rescue => e
|
25
|
+
raise e
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if __FILE__ == $0
|
32
|
+
url = ARGV.shift || abort("Usage: feed_parser.rb <url>")
|
33
|
+
rss_results = Automatic::FeedParser.get_rss(url)
|
34
|
+
require 'pp'
|
35
|
+
pp links
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# Name:: Automatic::Log
|
4
|
+
# Author:: 774 <http://id774.net>
|
5
|
+
# Created:: Feb 20, 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
|
11
|
+
module Log
|
12
|
+
def self.puts(level, message)
|
13
|
+
t = Time.now.strftime("%Y/%m/%d %X")
|
14
|
+
print "#{t} [#{level}] #{message}\n"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if __FILE__ == $0
|
20
|
+
level = ARGV.shift || abort("Usage: log.rb <level> <message>")
|
21
|
+
message = ARGV.shift
|
22
|
+
Automatic::Log.puts(level, message)
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# Name:: Automatic::Pipeline
|
4
|
+
# Author:: 774 <http://id774.net>
|
5
|
+
# Created:: Feb 22, 2012
|
6
|
+
# Updated:: Mar 3, 2012
|
7
|
+
# Copyright:: 774 Copyright (c) 2012
|
8
|
+
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
9
|
+
|
10
|
+
require 'active_support/core_ext'
|
11
|
+
|
12
|
+
module Automatic
|
13
|
+
module Plugin end
|
14
|
+
|
15
|
+
module Pipeline
|
16
|
+
def self.load_plugin(module_name)
|
17
|
+
Dir.foreach(Automatic.plugins_dir) do |subdir|
|
18
|
+
if /^#{subdir}_(.*)$/ =~ module_name.underscore
|
19
|
+
path = Automatic.plugins_dir + subdir + "/#{$1}.rb"
|
20
|
+
Automatic::Plugin.autoload module_name.to_sym, path.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.run(recipe)
|
26
|
+
raise "NoRecipeError" if recipe.nil?
|
27
|
+
pipeline = []
|
28
|
+
recipe.each_plugin { |plugin|
|
29
|
+
load_plugin(plugin.module)
|
30
|
+
klass = Automatic::Plugin.const_get(plugin.module)
|
31
|
+
pipeline = klass.new(plugin.config, pipeline).run
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Name:: Automatic::Recipe
|
2
|
+
# Author:: 774 <http://id774.net>
|
3
|
+
# Version:: 12.02-devel
|
4
|
+
# Created:: Feb 18, 2012
|
5
|
+
# Updated:: Feb 24, 2012
|
6
|
+
# Copyright:: 774 Copyright (c) 2012
|
7
|
+
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
8
|
+
|
9
|
+
require 'hashie'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
module Automatic
|
13
|
+
class Recipe
|
14
|
+
attr_reader :procedure
|
15
|
+
|
16
|
+
def initialize(path)
|
17
|
+
path = Automatic.config_dir + 'default.yml' if path.to_s.empty?
|
18
|
+
load_recipe(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def load_recipe(path)
|
22
|
+
@procedure = Hashie::Mash.new(YAML.load(File.read(path)))
|
23
|
+
end
|
24
|
+
|
25
|
+
def each_plugin
|
26
|
+
@procedure.plugins.each { |plugin|
|
27
|
+
yield plugin
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# Name:: Automatic::Config::Validator
|
4
|
+
# Author:: aerith <http://aerith.sc/>
|
5
|
+
# Created:: Feb 23, 2012
|
6
|
+
# Updated:: Feb 23, 2012
|
7
|
+
# Copyright:: Copyright (c) 2012
|
8
|
+
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
9
|
+
|
10
|
+
class Automatic
|
11
|
+
module Config
|
12
|
+
class Validator
|
13
|
+
attr_accessor :config, :result
|
14
|
+
|
15
|
+
class InvalidException < Exception; end;
|
16
|
+
|
17
|
+
def initialize(config = nil)
|
18
|
+
@config = config || {}
|
19
|
+
@result = ValidateResult.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate(klass, config = nil)
|
23
|
+
return true if config.nil?
|
24
|
+
return true if not config.is_a?(Hash)
|
25
|
+
return true if not klass.respond_to?(:rules)
|
26
|
+
|
27
|
+
rules = klass.rules
|
28
|
+
return true if not rules.is_a?(Hash)
|
29
|
+
|
30
|
+
process(rules, config).result.valid?
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def process(rules, config)
|
35
|
+
errors = {}
|
36
|
+
|
37
|
+
rules.each do |key, rule|
|
38
|
+
name = key.to_sym
|
39
|
+
|
40
|
+
rule.each do |method, args|
|
41
|
+
unless ValidateRule.send(method, *[config, name, args])
|
42
|
+
errors[name] = {} if errors[name].nil? or errors.size < 1
|
43
|
+
errors[name][method] = true
|
44
|
+
if @config["stop_on_invalid"] and errors[name][method]
|
45
|
+
raise InvalidException
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@result.errors = errors
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
class ValidateResult
|
57
|
+
attr_accessor :errors
|
58
|
+
|
59
|
+
def error(name)
|
60
|
+
errors.exists?(name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def valid?
|
64
|
+
errors.empty?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module ValidateRule
|
69
|
+
def self.required(config, name, *args)
|
70
|
+
return true unless args.size < 1 or args.first
|
71
|
+
return true if not config[name.to_s].nil? and not config[name.to_s].empty?
|
72
|
+
false
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.default(config, name, *args)
|
76
|
+
config[name.to_s] ||= args.first
|
77
|
+
true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Name:: Automatic::Plugin::CustomFeed::SVNLog
|
3
|
+
# Author:: kzgs
|
4
|
+
# Created:: Feb 29, 2012
|
5
|
+
# Updated:: Mar 3, 2012
|
6
|
+
# Copyright:: kzgs Copyright (c) 2012
|
7
|
+
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
8
|
+
|
9
|
+
require 'rss/maker'
|
10
|
+
|
11
|
+
module Automatic::Plugin
|
12
|
+
class CustomFeedSVNLog
|
13
|
+
require 'xmlsimple'
|
14
|
+
|
15
|
+
def initialize(config, pipeline=[])
|
16
|
+
@config = config
|
17
|
+
@pipeline = pipeline
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
revisions = XmlSimple.xml_in(`svn log #{svn_log_argument}`)["logentry"]
|
22
|
+
@pipeline << RSS::Maker.make("1.0") { |maker|
|
23
|
+
maker.channel.title = @config["title"] || ""
|
24
|
+
maker.channel.about = ""
|
25
|
+
maker.channel.description = ""
|
26
|
+
maker.channel.link = base_url
|
27
|
+
revisions.each { |rev|
|
28
|
+
item = maker.items.new_item
|
29
|
+
item.title = "#{rev["msg"]} by #{rev["author"]}"
|
30
|
+
item.link = base_url+"/!svn/bc/#{rev["revision"]}"
|
31
|
+
item.date = Time.parse(rev["date"][0])
|
32
|
+
}
|
33
|
+
}
|
34
|
+
return @pipeline
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def base_url
|
40
|
+
return @config['target'].gsub(/\/$/, "")
|
41
|
+
end
|
42
|
+
|
43
|
+
def svn_log_argument
|
44
|
+
return [
|
45
|
+
base_url,
|
46
|
+
"--xml",
|
47
|
+
"--limit=#{limit}"
|
48
|
+
].join(" ")
|
49
|
+
end
|
50
|
+
|
51
|
+
def limit
|
52
|
+
return @config['fetch_items'] || 30
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# Name:: Automatic::Plugin::Filter::Ignore
|
4
|
+
# Author:: 774 <http://id774.net>
|
5
|
+
# Created:: Feb 22, 2012
|
6
|
+
# Updated:: Mar 4, 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 FilterIgnore
|
12
|
+
def initialize(config, pipeline=[])
|
13
|
+
@config = config
|
14
|
+
@pipeline = pipeline
|
15
|
+
end
|
16
|
+
|
17
|
+
def exclude(items)
|
18
|
+
detection = false
|
19
|
+
unless @config['title'].nil?
|
20
|
+
@config['title'].each {|e|
|
21
|
+
if items.title.include?(e.chomp)
|
22
|
+
detection = true
|
23
|
+
Automatic::Log.puts("info", "Excluded by title: #{items.link}")
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
unless @config['link'].nil?
|
28
|
+
@config['link'].each {|e|
|
29
|
+
if items.link.include?(e.chomp)
|
30
|
+
detection = true
|
31
|
+
Automatic::Log.puts("info", "Excluded by link: #{items.link}")
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
unless @config['description'].nil?
|
36
|
+
@config['description'].each {|e|
|
37
|
+
if items.description.include?(e.chomp)
|
38
|
+
detection = true
|
39
|
+
Automatic::Log.puts("info", "Excluded by description: #{items.link}")
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
detection
|
44
|
+
end
|
45
|
+
|
46
|
+
def run
|
47
|
+
return_feeds = []
|
48
|
+
@pipeline.each {|feeds|
|
49
|
+
ignore = false
|
50
|
+
unless feeds.nil?
|
51
|
+
feeds.items.each {|items|
|
52
|
+
ignore = true if exclude(items)
|
53
|
+
}
|
54
|
+
end
|
55
|
+
return_feeds << feeds unless ignore
|
56
|
+
}
|
57
|
+
return_feeds
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# Name:: Automatic::Plugin::Filter::Image
|
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 FilterImage
|
12
|
+
require 'net/http'
|
13
|
+
require 'kconv'
|
14
|
+
|
15
|
+
def initialize(config, pipeline=[])
|
16
|
+
@config = config
|
17
|
+
@pipeline = pipeline
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_array(string)
|
21
|
+
array = Array.new
|
22
|
+
string.scan(/<img src="(.*?)"/) { |matched|
|
23
|
+
array = array | matched
|
24
|
+
}
|
25
|
+
return array
|
26
|
+
end
|
27
|
+
|
28
|
+
def run
|
29
|
+
return_feeds = []
|
30
|
+
@pipeline.each {|feeds|
|
31
|
+
img_url = ""
|
32
|
+
unless feeds.nil?
|
33
|
+
feeds.items.each {|feed|
|
34
|
+
arr = parse_array(feed.description)
|
35
|
+
if arr.length > 0
|
36
|
+
feed.link = arr[0]
|
37
|
+
else
|
38
|
+
feed.link = nil
|
39
|
+
end
|
40
|
+
}
|
41
|
+
end
|
42
|
+
return_feeds << feeds
|
43
|
+
}
|
44
|
+
return_feeds
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|