automatic 14.12.2 → 26.08
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.
- checksums.yaml +5 -5
- data/README.md +635 -83
- data/VERSION +1 -1
- data/automatic.gemspec +109 -248
- data/bin/automatic +20 -139
- data/config/feed2console.yml +10 -4
- data/config/feed2markdown.yml +41 -0
- data/doc/AI_TUTORIAL.md +518 -0
- data/doc/BASIC_DESIGN.md +516 -0
- data/doc/COPYING.LESSER +165 -0
- data/doc/DEPLOYMENT.md +824 -0
- data/doc/LICENSE.md +14 -0
- data/doc/PLUGINS.md +1875 -0
- data/doc/PLUGIN_DEVELOPMENT.md +86 -0
- data/doc/POLICY.md +857 -0
- data/doc/QUICKSTART.md +256 -0
- data/doc/RELEASING.md +381 -0
- data/doc/REQUIREMENTS.md +526 -0
- data/doc/VERSIONS +208 -0
- data/lib/automatic/cli.rb +248 -0
- data/lib/automatic/environment.rb +31 -5
- data/lib/automatic/feed_maker.rb +10 -9
- data/lib/automatic/feed_parser.rb +51 -35
- data/lib/automatic/http.rb +107 -0
- data/lib/automatic/log.rb +49 -18
- data/lib/automatic/opml.rb +3 -1
- data/lib/automatic/pipeline.rb +63 -32
- data/lib/automatic/recipe.rb +56 -17
- data/lib/automatic/version.rb +14 -1
- data/lib/automatic.rb +78 -20
- data/plugins/custom_feed/svn_log.rb +73 -32
- data/plugins/custom_feed/web.rb +348 -0
- data/plugins/filter/absolute_uri.rb +43 -27
- data/plugins/filter/accept.rb +38 -45
- data/plugins/filter/claude.rb +217 -0
- data/plugins/filter/clear.rb +12 -8
- data/plugins/filter/description_link.rb +49 -51
- data/plugins/filter/full_feed.rb +158 -52
- data/plugins/filter/gemini.rb +216 -0
- data/plugins/filter/github_feed.rb +38 -26
- data/plugins/filter/ignore.rb +33 -44
- data/plugins/filter/image.rb +36 -25
- data/plugins/filter/image_source.rb +58 -52
- data/plugins/filter/join.rb +107 -0
- data/plugins/filter/one.rb +19 -26
- data/plugins/filter/open_ai.rb +198 -0
- data/plugins/filter/rand.rb +16 -17
- data/plugins/filter/sakura_ai.rb +205 -0
- data/plugins/filter/sanitize.rb +29 -34
- data/plugins/filter/sort.rb +20 -27
- data/plugins/filter/tumblr_resize.rb +31 -23
- data/plugins/notify/ikachan.rb +86 -48
- data/plugins/provide/fluentd.rb +43 -24
- data/plugins/publish/amazon_s3.rb +73 -40
- data/plugins/publish/console.rb +19 -16
- data/plugins/publish/console_link.rb +20 -17
- data/plugins/publish/eject.rb +48 -26
- data/plugins/publish/fluentd.rb +50 -30
- data/plugins/publish/hatena_bookmark.rb +88 -71
- data/plugins/publish/instapaper.rb +69 -59
- data/plugins/publish/markdown.rb +278 -0
- data/plugins/publish/memcached.rb +35 -29
- data/plugins/store/database.rb +50 -48
- data/plugins/store/digest.rb +212 -0
- data/plugins/store/file.rb +99 -68
- data/plugins/store/full_text.rb +32 -25
- data/plugins/store/permalink.rb +18 -22
- data/plugins/subscription/feed.rb +34 -21
- data/plugins/subscription/link.rb +31 -32
- data/plugins/subscription/text.rb +32 -46
- data/plugins/subscription/tumblr.rb +55 -44
- data/plugins/subscription/xml.rb +40 -36
- metadata +108 -321
- data/Gemfile +0 -38
- data/Rakefile +0 -59
- data/doc/ChangeLog +0 -303
- data/doc/PLUGINS +0 -750
- data/doc/PLUGINS.ja +0 -753
- data/doc/README +0 -511
- data/doc/README.ja +0 -519
- data/plugins/filter/google_news.rb +0 -50
- data/plugins/publish/google_calendar.rb +0 -84
- data/plugins/publish/hipchat.rb +0 -46
- data/plugins/publish/pocket.rb +0 -45
- data/plugins/publish/twitter.rb +0 -58
- data/plugins/subscription/chan_toru.rb +0 -57
- data/plugins/subscription/g_guide.rb +0 -57
- data/plugins/subscription/pocket.rb +0 -51
- data/plugins/subscription/twitter.rb +0 -69
- data/plugins/subscription/twitter_search.rb +0 -50
- data/plugins/subscription/weather.rb +0 -33
- data/script/build +0 -84
- data/spec/fixtures/sampleFeeds.tsv +0 -1
- data/spec/fixtures/sampleFeeds2.tsv +0 -2
- data/spec/fixtures/sampleRecipe.yml +0 -24
- data/spec/lib/automatic/log_spec.rb +0 -32
- data/spec/lib/automatic/pipeline_spec.rb +0 -68
- data/spec/lib/automatic/recipe_spec.rb +0 -40
- data/spec/lib/automatic_spec.rb +0 -99
- data/spec/plugins/custom_feed/svn_log_spec.rb +0 -31
- data/spec/plugins/filter/absolute_uri_spec.rb +0 -61
- data/spec/plugins/filter/accept_spec.rb +0 -331
- data/spec/plugins/filter/clear_spec.rb +0 -49
- data/spec/plugins/filter/description_link_spec.rb +0 -138
- data/spec/plugins/filter/full_feed_spec.rb +0 -129
- data/spec/plugins/filter/github_feed_spec.rb +0 -55
- data/spec/plugins/filter/google_news_spec.rb +0 -69
- data/spec/plugins/filter/ignore_spec.rb +0 -328
- data/spec/plugins/filter/image_source_spec.rb +0 -89
- data/spec/plugins/filter/image_spec.rb +0 -65
- data/spec/plugins/filter/one_spec.rb +0 -71
- data/spec/plugins/filter/rand_spec.rb +0 -52
- data/spec/plugins/filter/sanitize_spec.rb +0 -153
- data/spec/plugins/filter/sort_spec.rb +0 -189
- data/spec/plugins/filter/tumblr_resize_spec.rb +0 -109
- data/spec/plugins/notify/ikachan_spec.rb +0 -58
- data/spec/plugins/provide/fluentd_spec.rb +0 -49
- data/spec/plugins/publish/amazon_s3_spec.rb +0 -40
- data/spec/plugins/publish/console_spec.rb +0 -30
- data/spec/plugins/publish/eject_spec.rb +0 -40
- data/spec/plugins/publish/fluentd_spec.rb +0 -40
- data/spec/plugins/publish/google_calendar_spec.rb +0 -83
- data/spec/plugins/publish/hatena_bookmark_spec.rb +0 -134
- data/spec/plugins/publish/hipchat_spec.rb +0 -69
- data/spec/plugins/publish/instapaper_spec.rb +0 -82
- data/spec/plugins/publish/memcached_spec.rb +0 -63
- data/spec/plugins/publish/pocket_spec.rb +0 -51
- data/spec/plugins/publish/twitter_spec.rb +0 -73
- data/spec/plugins/store/file_spec.rb +0 -58
- data/spec/plugins/store/full_text_spec.rb +0 -152
- data/spec/plugins/store/permalink_spec.rb +0 -206
- data/spec/plugins/subscription/chan_toru_spec.rb +0 -56
- data/spec/plugins/subscription/feed_spec.rb +0 -71
- data/spec/plugins/subscription/g_guide_spec.rb +0 -82
- data/spec/plugins/subscription/link_spec.rb +0 -72
- data/spec/plugins/subscription/pocket_spec.rb +0 -57
- data/spec/plugins/subscription/text_spec.rb +0 -84
- data/spec/plugins/subscription/tumblr_spec.rb +0 -74
- data/spec/plugins/subscription/twitter_search_spec.rb +0 -57
- data/spec/plugins/subscription/twitter_spec.rb +0 -73
- data/spec/plugins/subscription/weather_spec.rb +0 -44
- data/spec/plugins/subscription/xml_spec.rb +0 -84
- data/spec/spec_helper.rb +0 -106
- data/spec/user_dir/plugins/store/mock.rb +0 -16
- data/test/fixtures/sampleOPML.xml +0 -11
- data/test/integration/test_absoluteurl.yml +0 -25
- data/test/integration/test_activerecord.yml +0 -24
- data/test/integration/test_add_pocket.yml +0 -26
- data/test/integration/test_chan_toru.yml +0 -21
- data/test/integration/test_descriptionlink.yml +0 -21
- data/test/integration/test_fluentd.yml +0 -22
- data/test/integration/test_fulltext.yml +0 -30
- data/test/integration/test_google_news.yml +0 -21
- data/test/integration/test_googlealert.yml +0 -21
- data/test/integration/test_hatenabookmark.yml +0 -30
- data/test/integration/test_ignore.yml +0 -25
- data/test/integration/test_ignore2.yml +0 -22
- data/test/integration/test_image2local.yml +0 -33
- data/test/integration/test_instapaper.yml +0 -26
- data/test/integration/test_link2local.yml +0 -34
- data/test/integration/test_one.yml +0 -23
- data/test/integration/test_pocket.yml +0 -22
- data/test/integration/test_rand.yml +0 -21
- data/test/integration/test_sanitize.yml +0 -23
- data/test/integration/test_sort.yml +0 -36
- data/test/integration/test_svnlog.yml +0 -15
- data/test/integration/test_text2feed.yml +0 -36
- data/test/integration/test_tumblr2local.yml +0 -43
- data/test/integration/test_twitter_search.yml +0 -22
- data/test/integration/test_weather.yml +0 -19
- data/test/integration/test_xml2fluentd.yml +0 -21
- data/vendor/.gitkeep +0 -0
data/plugins/filter/sanitize.rb
CHANGED
|
@@ -1,49 +1,44 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
# Name::
|
|
3
|
-
# Author
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
2
|
+
# Name:: Automatic::Plugin::Filter::Sanitize
|
|
3
|
+
# Author: id774 (More info: http://id774.net)
|
|
4
|
+
# Source Code:: https://github.com/id774/automaticruby
|
|
5
|
+
# License:: The GPL version 3, or LGPL version 3 (Dual License).
|
|
6
|
+
# Contact:: idnanashi@gmail.com
|
|
7
|
+
# Created:: Jun 20, 2013
|
|
8
|
+
# Updated:: Aug 15, 2026
|
|
9
|
+
# Copyright:: Copyright (c) 2012-2026 Automatic Ruby Developers.
|
|
8
10
|
|
|
9
11
|
module Automatic::Plugin
|
|
10
12
|
class FilterSanitize
|
|
11
|
-
|
|
13
|
+
Automatic.require_optional('sanitize', needed_by: 'FilterSanitize')
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
MODES = {
|
|
16
|
+
'basic' => Sanitize::Config::BASIC,
|
|
17
|
+
'relaxed' => Sanitize::Config::RELAXED
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
def initialize(config, pipeline = [])
|
|
21
|
+
@config = config || {}
|
|
15
22
|
@pipeline = pipeline
|
|
16
|
-
|
|
17
|
-
when "basic"
|
|
18
|
-
@mode = Sanitize::Config::BASIC
|
|
19
|
-
when "relaxed"
|
|
20
|
-
@mode = Sanitize::Config::RELAXED
|
|
21
|
-
else
|
|
22
|
-
@mode = Sanitize::Config::RESTRICTED
|
|
23
|
-
end
|
|
23
|
+
@mode = MODES.fetch(@config['mode'].to_s, Sanitize::Config::RESTRICTED)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# Strips HTML from descriptions.
|
|
26
27
|
def run
|
|
27
|
-
@
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@return_feeds << feeds
|
|
34
|
-
end
|
|
35
|
-
}
|
|
36
|
-
@return_feeds
|
|
28
|
+
@pipeline.each_with_object([]) do |feeds, returned|
|
|
29
|
+
next if feeds.nil?
|
|
30
|
+
|
|
31
|
+
feeds.items.each { |item| sanitize(item) }
|
|
32
|
+
returned << feeds
|
|
33
|
+
end
|
|
37
34
|
end
|
|
38
35
|
|
|
39
36
|
private
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
end
|
|
46
|
-
feed
|
|
37
|
+
|
|
38
|
+
def sanitize(item)
|
|
39
|
+
item.description = Sanitize.fragment(item.description, @mode) unless item.description.nil?
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
Automatic::Log.puts('warn', "Undefined field detected in feed: #{e.message}")
|
|
47
42
|
end
|
|
48
43
|
end
|
|
49
44
|
end
|
data/plugins/filter/sort.rb
CHANGED
|
@@ -1,37 +1,30 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
# Name::
|
|
3
|
-
# Author
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
2
|
+
# Name:: Automatic::Plugin::Filter::Sort
|
|
3
|
+
# Author: id774 (More info: http://id774.net)
|
|
4
|
+
# Source Code:: https://github.com/id774/automaticruby
|
|
5
|
+
# License:: The GPL version 3, or LGPL version 3 (Dual License).
|
|
6
|
+
# Contact:: idnanashi@gmail.com
|
|
7
|
+
# Created:: Mar 23, 2012
|
|
8
|
+
# Updated:: Aug 15, 2026
|
|
9
|
+
# Copyright:: Copyright (c) 2012-2026 Automatic Ruby Developers.
|
|
8
10
|
|
|
9
11
|
module Automatic::Plugin
|
|
10
12
|
class FilterSort
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@
|
|
14
|
-
@
|
|
13
|
+
def initialize(config, pipeline = [])
|
|
14
|
+
@config = config || {}
|
|
15
|
+
@pipeline = pipeline
|
|
16
|
+
@ascending = @config['sort'].to_s == 'asc'
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
# Sorts each feed's items by date. Items must carry one; a feed built from
|
|
20
|
+
# a source without dates fails here.
|
|
17
21
|
def run
|
|
18
|
-
@
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
a.date <=> b.date
|
|
25
|
-
}
|
|
26
|
-
else
|
|
27
|
-
feeds.items.sort!{|a,b|
|
|
28
|
-
- (a.date <=> b.date)
|
|
29
|
-
}
|
|
30
|
-
end
|
|
31
|
-
@return_feeds << feeds
|
|
32
|
-
end
|
|
33
|
-
}
|
|
34
|
-
@return_feeds
|
|
22
|
+
@pipeline.each_with_object([]) do |feeds, returned|
|
|
23
|
+
next if feeds.nil?
|
|
24
|
+
|
|
25
|
+
feeds.items.sort! { |a, b| @ascending ? a.date <=> b.date : b.date <=> a.date }
|
|
26
|
+
returned << feeds
|
|
27
|
+
end
|
|
35
28
|
end
|
|
36
29
|
end
|
|
37
30
|
end
|
|
@@ -1,40 +1,48 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
# Name::
|
|
3
|
-
# Author
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
2
|
+
# Name:: Automatic::Plugin::Filter::TumblrResize
|
|
3
|
+
# Author: id774 (More info: http://id774.net)
|
|
4
|
+
# Source Code:: https://github.com/id774/automaticruby
|
|
5
|
+
# License:: The GPL version 3, or LGPL version 3 (Dual License).
|
|
6
|
+
# Contact:: idnanashi@gmail.com
|
|
7
|
+
# Created:: Feb 28, 2012
|
|
8
|
+
# Updated:: Aug 15, 2026
|
|
9
|
+
# Copyright:: Copyright (c) 2012-2026 Automatic Ruby Developers.
|
|
8
10
|
|
|
9
11
|
module Automatic::Plugin
|
|
10
12
|
class FilterTumblrResize
|
|
13
|
+
# Tumblr has served images under two URL schemes. The older one carries
|
|
14
|
+
# the size as a suffix on the file name -- tumblr_xxx_500.jpg -- and is
|
|
15
|
+
# what images uploaded before 2019 still use. The newer one carries it as
|
|
16
|
+
# a path segment -- /s540x810/ -- and is what everything since uses.
|
|
17
|
+
# Both are rewritten to the largest variant the scheme offers.
|
|
18
|
+
LEGACY_SIZE = /_(?:75sq|100|250|400|500)(?=\.[a-zA-Z0-9]+\z)/
|
|
19
|
+
LEGACY_LARGEST = '_1280'
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
PATH_SIZE = %r{/s\d+x\d+/}
|
|
22
|
+
PATH_LARGEST = '/s1280x1920/'
|
|
23
|
+
|
|
24
|
+
def initialize(config, pipeline = [])
|
|
25
|
+
@config = config || {}
|
|
14
26
|
@pipeline = pipeline
|
|
15
27
|
end
|
|
16
28
|
|
|
29
|
+
# Assumes FilterImage or FilterImageSource has already put an image URL in
|
|
30
|
+
# the link.
|
|
17
31
|
def run
|
|
18
|
-
@
|
|
19
|
-
@pipeline.each {|feeds|
|
|
20
|
-
img_url = ""
|
|
32
|
+
@pipeline.each_with_object([]) do |feeds, returned|
|
|
21
33
|
unless feeds.nil?
|
|
22
|
-
feeds.items.each
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
feeds.items.each do |item|
|
|
35
|
+
item.link = resize(item.link) unless item.link.nil?
|
|
36
|
+
end
|
|
25
37
|
end
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@return_feeds
|
|
38
|
+
returned << feeds
|
|
39
|
+
end
|
|
29
40
|
end
|
|
30
41
|
|
|
31
42
|
private
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
string = string.gsub("_250\.", "_1280\.")
|
|
36
|
-
string = string.gsub("_400\.", "_1280\.")
|
|
37
|
-
string = string.gsub("_500\.", "_1280\.")
|
|
43
|
+
|
|
44
|
+
def resize(link)
|
|
45
|
+
link.sub(LEGACY_SIZE, LEGACY_LARGEST).sub(PATH_SIZE, PATH_LARGEST)
|
|
38
46
|
end
|
|
39
47
|
end
|
|
40
48
|
end
|
data/plugins/notify/ikachan.rb
CHANGED
|
@@ -1,87 +1,125 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
# Name:: Automatic::Plugin::Notify::Ikachan
|
|
3
|
+
# Author: id774 (More info: http://id774.net)
|
|
4
|
+
# Source Code:: https://github.com/id774/automaticruby
|
|
5
|
+
# License:: The GPL version 3, or LGPL version 3 (Dual License).
|
|
6
|
+
# Contact:: idnanashi@gmail.com
|
|
7
|
+
# Created:: Mar 7, 2012
|
|
8
|
+
# Updated:: Aug 15, 2026
|
|
9
|
+
# Copyright:: Copyright (c) 2012-2026 Automatic Ruby Developers.
|
|
10
|
+
#
|
|
11
|
+
# Description:: Posts each item to an IRC channel through an ikachan
|
|
12
|
+
# HTTP-to-IRC gateway, which the operator runs themselves.
|
|
2
13
|
|
|
3
|
-
require '
|
|
4
|
-
require '
|
|
5
|
-
require 'active_support/deprecation'
|
|
14
|
+
require 'net/http'
|
|
15
|
+
require 'uri'
|
|
6
16
|
|
|
7
17
|
module Automatic::Plugin
|
|
8
18
|
class Ikachan
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
require 'net/http'
|
|
12
|
-
require 'uri'
|
|
19
|
+
OPEN_TIMEOUT = 10
|
|
20
|
+
READ_TIMEOUT = 30
|
|
13
21
|
|
|
14
22
|
attr_accessor :params
|
|
15
23
|
|
|
16
24
|
def initialize
|
|
17
|
-
@params = {
|
|
18
|
-
"url" => "",
|
|
19
|
-
"port" => "",
|
|
20
|
-
"channels" => [],
|
|
21
|
-
"command" => "",
|
|
22
|
-
}
|
|
25
|
+
@params = { 'url' => '', 'port' => '', 'channels' => [], 'command' => '' }
|
|
23
26
|
end
|
|
24
27
|
|
|
25
|
-
def post(link, title =
|
|
26
|
-
message
|
|
27
|
-
uri
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
http.
|
|
34
|
-
|
|
35
|
-
if res.code == "200"
|
|
36
|
-
Automatic::Log.puts(:info, "Success: #{message}")
|
|
37
|
-
else
|
|
38
|
-
Automatic::Log.puts(:error, "#{res.code} Error: #{message}")
|
|
39
|
-
end
|
|
28
|
+
def post(link, title = '')
|
|
29
|
+
message = build_message(link, title)
|
|
30
|
+
uri = endpoint
|
|
31
|
+
|
|
32
|
+
start(uri) do |http|
|
|
33
|
+
channels.each do |channel|
|
|
34
|
+
# Join first, in case the gateway is not in the channel.
|
|
35
|
+
http.request(form('/join', channel: channel))
|
|
36
|
+
response = http.request(form(uri.path, channel: channel, message: message))
|
|
37
|
+
log(response, message)
|
|
40
38
|
end
|
|
41
39
|
end
|
|
42
40
|
end
|
|
43
41
|
|
|
44
42
|
private
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
# A gateway reached over https is spoken to over https. The earlier
|
|
45
|
+
# version built a plain connection whatever the URL said.
|
|
46
|
+
def start(uri, &block)
|
|
47
|
+
proxy = Net::HTTP.Proxy(ENV.fetch('PROXY', nil), 8080)
|
|
48
|
+
proxy.start(uri.host, uri.port,
|
|
49
|
+
use_ssl: uri.scheme == 'https',
|
|
50
|
+
open_timeout: OPEN_TIMEOUT,
|
|
51
|
+
read_timeout: READ_TIMEOUT, &block)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Form-encoded by Net::HTTP rather than interpolated into the body, so
|
|
55
|
+
# that a title carrying an ampersand or a space reaches the channel as it
|
|
56
|
+
# was written instead of splitting the request.
|
|
57
|
+
def form(path, params)
|
|
58
|
+
request = Net::HTTP::Post.new(path)
|
|
59
|
+
request.set_form_data(params)
|
|
60
|
+
request
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def channels
|
|
64
|
+
value = @params['channels']
|
|
65
|
+
value.is_a?(Array) ? value : value.to_s.split(',')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def endpoint
|
|
69
|
+
URI.parse("#{@params['url']}:#{@params['port']}/#{@params['command']}")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def log(response, message)
|
|
73
|
+
if response.code == '200'
|
|
74
|
+
Automatic::Log.puts(:info, "Success: #{message}")
|
|
75
|
+
else
|
|
76
|
+
Automatic::Log.puts(:error, "#{response.code} Error: #{message}")
|
|
77
|
+
end
|
|
48
78
|
end
|
|
49
79
|
|
|
50
80
|
def build_message(link, title)
|
|
51
|
-
message =
|
|
52
|
-
message += "#{title
|
|
53
|
-
message
|
|
54
|
-
message
|
|
81
|
+
message = ''
|
|
82
|
+
message += "#{title} - " unless title.nil? || title.to_s.empty?
|
|
83
|
+
message + link.to_s
|
|
55
84
|
end
|
|
56
85
|
end
|
|
57
86
|
|
|
58
87
|
class NotifyIkachan
|
|
59
88
|
attr_accessor :ikachan
|
|
60
89
|
|
|
61
|
-
def initialize(config, pipeline=[])
|
|
62
|
-
@config = config
|
|
90
|
+
def initialize(config, pipeline = [])
|
|
91
|
+
@config = config || {}
|
|
63
92
|
@pipeline = pipeline
|
|
64
93
|
|
|
65
94
|
@ikachan = Ikachan.new
|
|
66
95
|
@ikachan.params = {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
96
|
+
'url' => @config['url'],
|
|
97
|
+
'port' => @config['port'] || '4979',
|
|
98
|
+
'channels' => channels,
|
|
99
|
+
'command' => @config['command'] || 'notice'
|
|
71
100
|
}
|
|
72
101
|
end
|
|
73
102
|
|
|
103
|
+
# Returns the pipeline unchanged: a Notify plugin sends a notification and
|
|
104
|
+
# nothing else.
|
|
74
105
|
def run
|
|
75
|
-
@pipeline.each
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
106
|
+
@pipeline.each do |feeds|
|
|
107
|
+
next if feeds.nil?
|
|
108
|
+
|
|
109
|
+
feeds.items.each do |feed|
|
|
110
|
+
Automatic::Log.puts('info', "Ikachan: [#{feed.link}] sending to #{channels.join(',')}...")
|
|
111
|
+
ikachan.post(feed.link, feed.title)
|
|
112
|
+
sleep(@config['interval'].to_i)
|
|
82
113
|
end
|
|
83
|
-
|
|
114
|
+
end
|
|
84
115
|
@pipeline
|
|
85
116
|
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
# Comma separated, with a leading '#' added where it is absent.
|
|
121
|
+
def channels
|
|
122
|
+
@config['channels'].to_s.split(',').map { |name| name.start_with?('#') ? name : "##{name}" }
|
|
123
|
+
end
|
|
86
124
|
end
|
|
87
125
|
end
|
data/plugins/provide/fluentd.rb
CHANGED
|
@@ -1,37 +1,56 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
# Name::
|
|
3
|
-
# Author
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
2
|
+
# Name:: Automatic::Plugin::Provide::Fluentd
|
|
3
|
+
# Author: id774 (More info: http://id774.net)
|
|
4
|
+
# Source Code:: https://github.com/id774/automaticruby
|
|
5
|
+
# License:: The GPL version 3, or LGPL version 3 (Dual License).
|
|
6
|
+
# Contact:: idnanashi@gmail.com
|
|
7
|
+
# Created:: Jul 12, 2013
|
|
8
|
+
# Updated:: Aug 15, 2026
|
|
9
|
+
# Copyright:: Copyright (c) 2012-2026 Automatic Ruby Developers.
|
|
8
10
|
|
|
9
11
|
module Automatic::Plugin
|
|
10
12
|
class ProvideFluentd
|
|
11
|
-
|
|
13
|
+
Automatic.require_optional('fluent-logger', needed_by: 'ProvideFluentd')
|
|
12
14
|
|
|
13
|
-
def initialize(config, pipeline=[])
|
|
14
|
-
@config
|
|
15
|
+
def initialize(config, pipeline = [])
|
|
16
|
+
@config = config || {}
|
|
15
17
|
@pipeline = pipeline
|
|
16
|
-
@mode
|
|
17
|
-
@fluentd
|
|
18
|
-
host = @config['host'],
|
|
19
|
-
port = @config['port']) unless @mode == 'test'
|
|
18
|
+
@mode = @config['mode']
|
|
19
|
+
@fluentd = open_logger unless test?
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# Posts each item's content_encoded to Fluentd, which must be something
|
|
23
|
+
# Fluentd accepts as a record. Distinct from PublishFluentd, which posts
|
|
24
|
+
# the item's fields.
|
|
22
25
|
def run
|
|
23
|
-
@pipeline.each
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
rescue
|
|
29
|
-
Automatic::Log.puts("error", "Fluent::Logger.post failed, the content_encoded of item may be not kind of Hash.")
|
|
30
|
-
end
|
|
31
|
-
}
|
|
32
|
-
end
|
|
33
|
-
}
|
|
26
|
+
@pipeline.each do |feeds|
|
|
27
|
+
next if feeds.nil?
|
|
28
|
+
|
|
29
|
+
feeds.items.each { |feed| post(feed) }
|
|
30
|
+
end
|
|
34
31
|
@pipeline
|
|
35
32
|
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def open_logger
|
|
37
|
+
Fluent::Logger::FluentLogger.open(nil,
|
|
38
|
+
host: @config['host'],
|
|
39
|
+
port: @config['port'].to_i)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def post(feed)
|
|
43
|
+
return if test?
|
|
44
|
+
|
|
45
|
+
@fluentd.post(@config['tag'], feed.content_encoded)
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
Automatic::Log.puts('error',
|
|
48
|
+
'Fluent::Logger.post failed, the content_encoded of item may ' \
|
|
49
|
+
"be not kind of Hash: #{e.message}")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test?
|
|
53
|
+
@mode == 'test'
|
|
54
|
+
end
|
|
36
55
|
end
|
|
37
56
|
end
|
|
@@ -1,58 +1,91 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
# Name::
|
|
3
|
-
# Author
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
2
|
+
# Name:: Automatic::Plugin::Publish::AmazonS3
|
|
3
|
+
# Author: id774 (More info: http://id774.net)
|
|
4
|
+
# Source Code:: https://github.com/id774/automaticruby
|
|
5
|
+
# License:: The GPL version 3, or LGPL version 3 (Dual License).
|
|
6
|
+
# Contact:: idnanashi@gmail.com
|
|
7
|
+
# Created:: Feb 24, 2014
|
|
8
|
+
# Updated:: Aug 15, 2026
|
|
9
|
+
# Copyright:: Copyright (c) 2012-2026 Automatic Ruby Developers.
|
|
8
10
|
|
|
9
11
|
module Automatic::Plugin
|
|
10
12
|
class PublishAmazonS3
|
|
11
13
|
require 'uri'
|
|
12
|
-
require 'aws-sdk'
|
|
13
14
|
|
|
14
|
-
def initialize(config, pipeline=[])
|
|
15
|
-
@config
|
|
15
|
+
def initialize(config, pipeline = [])
|
|
16
|
+
@config = config || {}
|
|
16
17
|
@pipeline = pipeline
|
|
17
|
-
|
|
18
|
-
:access_key_id => @config['access_key'],
|
|
19
|
-
:secret_access_key => @config['secret_key']
|
|
20
|
-
)
|
|
21
|
-
@bucket = s3.buckets[@config['bucket_name']]
|
|
22
|
-
@mode = @config['mode']
|
|
18
|
+
@mode = @config['mode']
|
|
23
19
|
end
|
|
24
20
|
|
|
21
|
+
# Uploads the files whose link is a file URI, normally after StoreFile.
|
|
25
22
|
def run
|
|
26
|
-
@pipeline.each
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
uri = URI.parse(feed.link)
|
|
32
|
-
if uri.scheme == 'file'
|
|
33
|
-
upload_amazons3(uri.path, @config['target_path'])
|
|
34
|
-
else
|
|
35
|
-
Automatic::Log.puts("warn", "Skip feed due to uri scheme is not file.")
|
|
36
|
-
end
|
|
37
|
-
rescue
|
|
38
|
-
Automatic::Log.puts("error", "Error detected with #{feed.link} in uploading AmazonS3.")
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
}
|
|
42
|
-
end
|
|
43
|
-
}
|
|
23
|
+
@pipeline.each do |feeds|
|
|
24
|
+
next if feeds.nil?
|
|
25
|
+
|
|
26
|
+
feeds.items.each { |feed| publish(feed) }
|
|
27
|
+
end
|
|
44
28
|
@pipeline
|
|
45
29
|
end
|
|
46
30
|
|
|
47
31
|
private
|
|
48
32
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
def publish(feed)
|
|
34
|
+
return if feed.link.nil?
|
|
35
|
+
|
|
36
|
+
uri = URI.parse(feed.link)
|
|
37
|
+
if uri.scheme == 'file'
|
|
38
|
+
upload(uri.path)
|
|
39
|
+
else
|
|
40
|
+
Automatic::Log.puts('warn', 'Skip feed due to uri scheme is not file.')
|
|
41
|
+
end
|
|
42
|
+
rescue StandardError => e
|
|
43
|
+
Automatic::Log.puts('error',
|
|
44
|
+
"Error detected with #{feed.link} in uploading AmazonS3: #{e.message}")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def upload(path)
|
|
48
|
+
key = target_key(path)
|
|
49
|
+
File.open(path, 'rb') { |body| s3.put_object(bucket: bucket, key: key, body: body) } unless test?
|
|
50
|
+
Automatic::Log.puts('info', "Uploaded: file #{path} to the key #{key} on #{bucket}.")
|
|
51
|
+
[path, key]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def target_key(path)
|
|
55
|
+
File.join(@config['target_path'].to_s, File.basename(path)).sub(%r{\A/}, '')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def bucket
|
|
59
|
+
@config['bucket_name'].to_s
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test?
|
|
63
|
+
@mode == 'test'
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# AWS SDK for Ruby v3, which is the SDK AWS publishes and maintains. The
|
|
67
|
+
# gem is required here rather than at the top of the file, so that a
|
|
68
|
+
# Recipe running this plugin in `mode: test` -- and this plugin's own
|
|
69
|
+
# specs -- need neither the gem nor an account.
|
|
70
|
+
#
|
|
71
|
+
# Credentials are the Recipe's where it carries them, and the SDK's
|
|
72
|
+
# default chain -- environment, shared profile, instance role -- where it
|
|
73
|
+
# does not, which is the way to run this without a secret in a file.
|
|
74
|
+
def s3
|
|
75
|
+
@s3 ||= begin
|
|
76
|
+
Automatic.require_optional('aws-sdk-s3', needed_by: 'PublishAmazonS3')
|
|
77
|
+
Aws::S3::Client.new(**client_options)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def client_options
|
|
82
|
+
options = {}
|
|
83
|
+
options[:region] = @config['region'].to_s unless @config['region'].nil?
|
|
84
|
+
unless @config['access_key'].nil?
|
|
85
|
+
options[:access_key_id] = @config['access_key'].to_s
|
|
86
|
+
options[:secret_access_key] = @config['secret_key'].to_s
|
|
87
|
+
end
|
|
88
|
+
options
|
|
56
89
|
end
|
|
57
90
|
end
|
|
58
91
|
end
|
data/plugins/publish/console.rb
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
# Name::
|
|
3
|
-
# Author
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
2
|
+
# Name:: Automatic::Plugin::Publish::Console
|
|
3
|
+
# Author: id774 (More info: http://id774.net)
|
|
4
|
+
# Source Code:: https://github.com/id774/automaticruby
|
|
5
|
+
# License:: The GPL version 3, or LGPL version 3 (Dual License).
|
|
6
|
+
# Contact:: idnanashi@gmail.com
|
|
7
|
+
# Created:: Feb 23, 2012
|
|
8
|
+
# Updated:: Aug 15, 2026
|
|
9
|
+
# Copyright:: Copyright (c) 2012-2026 Automatic Ruby Developers.
|
|
8
10
|
|
|
9
11
|
module Automatic::Plugin
|
|
10
12
|
class PublishConsole
|
|
11
13
|
require 'pp'
|
|
12
14
|
|
|
13
|
-
def initialize(config, pipeline=[])
|
|
14
|
-
@config
|
|
15
|
+
def initialize(config, pipeline = [])
|
|
16
|
+
@config = config || {}
|
|
15
17
|
@pipeline = pipeline
|
|
16
|
-
|
|
18
|
+
# Held in an instance variable rather than written to $stdout directly,
|
|
19
|
+
# so that a spec can substitute it. See doc/PLUGINS.md section 3.6.
|
|
20
|
+
@output = $stdout
|
|
17
21
|
end
|
|
18
22
|
|
|
23
|
+
# Prints each item, the plugin to end a Recipe with while writing it.
|
|
19
24
|
def run
|
|
20
|
-
@pipeline.each
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
}
|
|
25
|
+
@pipeline.each do |feeds|
|
|
26
|
+
next if feeds.nil?
|
|
27
|
+
|
|
28
|
+
feeds.items.each { |feed| @output.puts(feed.pretty_inspect) }
|
|
29
|
+
end
|
|
27
30
|
@pipeline
|
|
28
31
|
end
|
|
29
32
|
end
|