goatless 0.1.0

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/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
+