goatless 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/goatless ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'goatless'
5
+
6
+ options = {}
7
+ optparse = OptionParser.new do |opts|
8
+ opts.banner = "Usage: goatless.rb -u username -p password -f path"
9
+ opts.on("-u", "--username [username]", "LJ Username") do |v|
10
+ options[:username] = v
11
+ end
12
+ opts.on("-p", "--password [password]", "LJ Password") do |v|
13
+ options[:password] = v
14
+ end
15
+ opts.on("-f", "--file [filepath]", "File to dump RSS feed") do |v|
16
+ options[:filename] = v
17
+ end
18
+ end
19
+ optparse.parse!
20
+
21
+
22
+ if [options[:username], options[:password], options[:filename]].include? nil
23
+ puts optparse
24
+ exit(-1)
25
+ end
26
+
27
+ goatless = Goatless::Goatless.new(options[:username], options[:password])
28
+ File.new(options[:filename], "w+").write(goatless.rss)
@@ -0,0 +1,76 @@
1
+ module Goatless
2
+ class FriendFetcher
3
+ attr_accessor :friend_url, :username, :password, :items
4
+
5
+ def initialize(friend_url, username, password)
6
+ puts friend_url
7
+ @friend_url = friend_url
8
+ @username = username
9
+ @password = password
10
+ end
11
+
12
+ def fetch
13
+ @patron_session = Patron::Session.new
14
+ @patron_session.username = @username
15
+ @patron_session.password = @password
16
+ @patron_session.timeout = 10
17
+ @patron_session.connect_timeout = 30000
18
+ @patron_session.auth_type = :digest
19
+ @items = []
20
+ xml = @patron_session.get("#{@friend_url}?auth=digest").body
21
+ parse(xml)
22
+ end
23
+
24
+ def parse(xml)
25
+ xml_doc = Nokogiri::XML::Reader(xml)
26
+ @storage = PStore.new('goatless.pstore') # Hope this can decrease memory usage
27
+
28
+ max_items = 50 # Move this to the CLI flag or config
29
+ current_item = nil
30
+ current_author = nil
31
+
32
+ xml_doc.each do |node|
33
+ next if max_items == 0
34
+ case node.name
35
+ when 'lj:journal'
36
+ current_author = node.inner_xml if current_author.nil?
37
+ when 'item'
38
+ if current_item.nil?
39
+ current_item = RssItem.new
40
+ else
41
+ current_item.author = current_author
42
+ if current_item.pub_date > (DateTime.now - 7) # do not add items older then week
43
+ @storage.transaction do
44
+ @storage[:items] ||= Array.new
45
+ unless @storage[:items].include? current_item
46
+ @storage[:items] << current_item
47
+ @storage.commit
48
+ end
49
+ end
50
+ end
51
+ current_item = nil
52
+ max_items -= 1
53
+ end
54
+ when 'link'
55
+ unless current_item.nil?
56
+ current_item.permalink = node.inner_xml if current_item.permalink.nil?
57
+ end
58
+ when 'pubDate'
59
+ current_item.pub_date = DateTime.parse(node.inner_xml) if current_item.pub_date.nil?
60
+ when 'description'
61
+ unless current_item.nil?
62
+ if current_item.body.nil?
63
+ current_item.body = node.inner_xml
64
+ end
65
+ end
66
+ when 'lj:security'
67
+ current_item.security_type = node.inner_xml if current_item.security_type.nil?
68
+ when 'title'
69
+ unless current_item.nil?
70
+ current_item.title = node.inner_xml if current_item.title.nil?
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,30 @@
1
+ module Goatless
2
+ class Goatless
3
+ def initialize(username, password)
4
+ @username, @password = username, password
5
+ @threads = []
6
+ friends_urls = OpmlFetcher.new(@username).fetch
7
+ friends_urls.each_slice(3) do |rss_urls|
8
+ @threads << Thread.new {
9
+ rss_urls.each do |rss_url|
10
+ friend_fetcher = FriendFetcher.new(rss_url, @username, @password)
11
+ begin
12
+ friend_fetcher.fetch
13
+ rescue Exception => e
14
+ # Still have a lot's of "connection timed out" here
15
+ puts e
16
+ end
17
+ end
18
+ }
19
+ end
20
+ @threads.each { |t| t.join }
21
+ end
22
+
23
+ def rss
24
+ @storage = PStore.new('goatless.pstore')
25
+ @storage.transaction(true) do
26
+ RssBuilder.new(@storage[:items]).document
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ module Goatless
2
+ class OpmlFetcher
3
+ attr_accessor :rss_urls
4
+
5
+ def initialize(username)
6
+ @url = "http://www.livejournal.com/tools/opml.bml?user=#{username}"
7
+ # @url = 'http://sdfgh153.ru/sample.opml'
8
+ end
9
+
10
+ def fetch
11
+ patron_session = Patron::Session.new
12
+ patron_session.timeout = 30
13
+ patron_session.connect_timeout = 30000
14
+ @rss_urls = []
15
+
16
+ opml_doc = Nokogiri::XML(patron_session.get(@url).body)
17
+ opml_doc.elements.xpath("//outline").each do |outline|
18
+ @rss_urls << outline['xmlUrl']
19
+ end
20
+ puts "You have #{@rss_urls.size} friends!"
21
+ @rss_urls
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ module Goatless
2
+ class RssBuilder
3
+
4
+ def initialize(items)
5
+ @items = items
6
+ end
7
+
8
+ def document
9
+ #TODO: Move number of items in feed into settings
10
+ items = @items.sort { |x, y| x.pub_date <=> y.pub_date }.last(100).reverse
11
+
12
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |rss|
13
+ rss.rss(:version => '2.0') {
14
+ rss.channel {
15
+ rss.title "Friends"
16
+ rss.generator "Goatless"
17
+ rss.description "Goatless friends"
18
+ rss.link "https://github.com/semka/goatless"
19
+
20
+ items.each do |i|
21
+ rss.item {
22
+ rss.pubDate i.pub_date.strftime("%a, %d %b %Y %H:%M:%S %z") # to RFC 822
23
+ rss.author i.author
24
+ rss.link i.permalink
25
+ if i.public?
26
+ rss.title i.title
27
+ rss.description do |d| d << i.body end
28
+ else
29
+ rss.title "Private post"
30
+ rss.description "Private post"
31
+ end
32
+ }
33
+ end
34
+ }
35
+ }
36
+ end
37
+ builder.to_xml
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ module Goatless
2
+
3
+ class RssItem
4
+ attr_accessor :pub_date, :xml_element, :security_type, :pub_date, :title, :body, :permalink, :author
5
+
6
+ def public?
7
+ @security_type == 'public'
8
+ end
9
+
10
+ def ==(an_rss_item)
11
+ self.permalink == an_rss_item.permalink && !self.permalink.nil?
12
+ end
13
+ end
14
+ end
data/lib/goatless.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'date'
3
+ require 'patron'
4
+ require 'nokogiri'
5
+ require 'pstore'
6
+
7
+ Dir.glob(File.join(File.dirname(__FILE__), 'goatless/**.rb')).each { |f| require f }
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: goatless
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Semka Novikov
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-19 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description:
22
+ email: semka.novikov@gmail.com
23
+ executables:
24
+ - goatless
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - bin/goatless
31
+ - lib/goatless/friend_fetcher.rb
32
+ - lib/goatless/goatless.rb
33
+ - lib/goatless/opml_fetcher.rb
34
+ - lib/goatless/rss_builder.rb
35
+ - lib/goatless/rss_item.rb
36
+ - lib/goatless.rb
37
+ homepage: http://yoursite.example.com
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.5
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: What this thing does
70
+ test_files: []
71
+