ffxiv 0.9.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3fb7ce29800d85a1d741dced5bb8ba1049b63115
4
+ data.tar.gz: 40595334045f34238ec0e636e896fb24f521252f
5
+ SHA512:
6
+ metadata.gz: cdfe1416572fa6daf7a401c00d48a3d87720777b531585230f7d82a0c5e539f4984e8be5f365c8f599ab384a59261245a51132256a04022a7b051391242f8f23
7
+ data.tar.gz: a5951abe15687d137cee233c7fd2fda8a224d73cc5d016caf7b5688e0baaeb91f1d1d9f3baaa4bdadde382b8f39d51f055321567cdac4ae861ed10454a8f1e22
data/lib/ffxiv.rb ADDED
@@ -0,0 +1,6 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require "ffxiv/lodestone"
4
+
5
+ module FFXIV
6
+ end
@@ -0,0 +1,24 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require "pp"
4
+ require "nokogiri"
5
+ require "uri"
6
+ require "open-uri"
7
+
8
+ require "lodestone/model"
9
+ require "lodestone/character"
10
+ require "lodestone/free-company"
11
+
12
+ module FFXIV
13
+ module Lodestone
14
+ class << self
15
+ def fetch(endpoint)
16
+ uri = "http://na.finalfantasyxiv.com/lodestone/" + endpoint
17
+ html, charset = open(uri) do |page|
18
+ [page.read, page.charset]
19
+ end
20
+ Nokogiri::HTML.parse(html, nil, charset)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,112 @@
1
+ module FFXIV
2
+ module Lodestone
3
+ class Character < Model
4
+
5
+ attr_accessor :id, :name, :server, :thumbnail_uri, :image_uri, :race, :subrace, :gender,
6
+ :nameday, :guardian, :city_state, :grand_company, :grand_company_rank,
7
+ :free_company, :minions, :mounts, :end_contents, :self_introduction, :classes,
8
+ :num_blogs, :first_blogged, :latest_blogged, :bpd, :free_company_rank
9
+ alias :end_contents? :end_contents
10
+
11
+ class << self
12
+
13
+ def name_to_id(name, server)
14
+ dom = Lodestone.fetch("character/?q=#{URI.escape(name)}&worldname=#{URI.escape(server)}")
15
+ dom.at("h4.player_name_gold a").attr("href").split("/")[-1].to_i
16
+ end
17
+
18
+ def find_by_id(id)
19
+ begin
20
+ dom = Lodestone.fetch("character/#{id}")
21
+
22
+ props = {}
23
+ props[:id] = id
24
+ props[:name] = dom.css("div.player_name_txt h2 a").inner_text
25
+ props[:server] = dom.css("div.player_name_txt h2 span").inner_text[2...-1]
26
+ props[:thumbnail_uri] = drop_uts(dom.css("div.player_name_thumb img").attr("src").inner_text)
27
+ props[:image_uri] = drop_uts(dom.css("img[width='264']").attr("src").inner_text)
28
+ props[:race], props[:subrace], props[:gender] = dom.css("div.chara_profile_title").inner_text.strip.split(" / ")
29
+ dom.css("ul.chara_profile_list li").to_a.each do |n|
30
+ t = n.inner_text
31
+ if t.include?("Nameday")
32
+ props[:nameday], props[:guardian] = n.css(".txt_yellow").to_a.map(&:inner_text)
33
+ elsif t.include?("City-state")
34
+ props[:city_state] = n.css(".txt_yellow").inner_text
35
+ elsif t.include?("Grand Company")
36
+ props[:grand_company], props[:grand_company_rank] = n.css(".txt_yellow").inner_text.split("/")
37
+ elsif t.include?("Free Company")
38
+ props[:free_company] = n.css(".txt_yellow").inner_text
39
+ end
40
+ end
41
+ dom.css("div.area_header_w358_inner").to_a.each do |n|
42
+ aname = n.css("h4.ic_silver").inner_text.downcase
43
+ items = n.css("a.ic_reflection_box").to_a.map{|m| m.attr("title")}.map{|i| i.capitalize.gsub(/[\s][a-z]/) {|s| s.upcase}}.sort
44
+ props[:"#{aname}"] = items
45
+ end
46
+ props[:end_contents] = props[:mounts].include?("Magitek Armor")
47
+ props[:self_introduction] = dom.css("div.txt_selfintroduction").inner_html.strip
48
+ props[:classes] = {}
49
+ %w{fighter sorcerer crafter gatherer}.each do |discipline|
50
+ dom.css("h4.class_#{discipline} + div.table_black_w626 td.ic_class_wh24_box").to_a.each do |td|
51
+ txt = td.inner_text
52
+ if !txt.empty?
53
+ lvl = td.next_sibling().next_sibling().inner_text.to_i
54
+ props[:classes][txt] = lvl == 0 ? nil : lvl
55
+ end
56
+ end
57
+ end
58
+
59
+ self.new(props)
60
+ rescue => e
61
+ pp e
62
+ nil
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ def num_blogs
69
+ init_blog; @num_blogs
70
+ end
71
+
72
+ def bpd
73
+ init_blog; @bpd
74
+ end
75
+
76
+ def latest_blogged
77
+ init_blog; @latest_blogged
78
+ end
79
+
80
+ def first_blogged
81
+ init_blog; @first_blogged
82
+ end
83
+
84
+ def thumbnail_uri
85
+ @thumbnail_uri + "?#{Time.now.to_i}"
86
+ end
87
+
88
+ def image_uri
89
+ @image_uri + "?#{Time.now.to_i}"
90
+ end
91
+
92
+ private
93
+ def init_blog
94
+ if @num_blogs.nil?
95
+ dom1 = Lodestone.fetch("character/#{@id}/blog?order=1")
96
+ total_node = dom1.at("div.current_list span.total")
97
+ @num_blogs = total_node.nil? ? 0 : total_node.inner_text.to_i
98
+ if @num_blogs > 0
99
+ dom2 = Lodestone.fetch("character/#{@id}/blog?order=2")
100
+ {latest_blogged: dom1, first_blogged: dom2}.each do |prop, dom|
101
+ txt = dom.at("h3.header_title").inner_text
102
+ uts = txt.match(/ldst_strftime\((\d{10}), 'YMDHM'\)/)[1].to_i
103
+ send("#{prop}=", Time.at(uts).utc)
104
+ end
105
+ @bpd = (@num_blogs / ((Time.now - @first_blogged) / 86400.0)).round(2)
106
+ end
107
+ end
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,116 @@
1
+ module FFXIV
2
+ module Lodestone
3
+ class FreeCompany < Model
4
+
5
+ attr_accessor :id, :name, :server, :tag, :logo_uri, :formed, :members, :num_members, :rank,
6
+ :grand_company, :grand_company_standing, :slogan, :focus, :seeking, :active,
7
+ :recruiting, :housing, :weekly_rank, :monthly_rank
8
+ alias :recruiting? :recruiting
9
+
10
+ class << self
11
+
12
+ def name_to_id(name, server)
13
+ dom = Lodestone.fetch("freecompany/?q=#{URI.escape(name)}&worldname=#{URI.escape(server)}")
14
+ dom.at("div.ic_freecompany_box a").attr("href").split("/")[-1].to_i
15
+ end
16
+
17
+ def find_by_id(id)
18
+ begin
19
+ dom = Lodestone.fetch("freecompany/#{id}")
20
+
21
+ props = {}
22
+ props[:id] = id
23
+
24
+ props[:logo_uri] = dom.css("div.ic_crest_64 img").to_a.map{|n| n.attr("src")}
25
+
26
+ gcs_node = dom.at("span.friendship_color")
27
+ props[:grand_company_standing] = gcs_node.inner_text[1...-1]
28
+
29
+ gc_node = gcs_node.previous_sibling
30
+ props[:grand_company] = gc_node.inner_text.strip
31
+
32
+ name_node = gcs_node.next_sibling.next_sibling.next_sibling
33
+ props[:name] = name_node.inner_text
34
+
35
+ server_node = name_node.next_sibling.next_sibling
36
+ props[:server] = server_node.inner_text[1...-1]
37
+
38
+ props[:tag] = dom.at("td.vm").last_element_child.next_sibling.inner_text[1...-1]
39
+
40
+ dom.css("table.table_style2 tr").to_a.each do |tr|
41
+ td_node = tr.at("td")
42
+ td_text = td_node.inner_text.strip
43
+ case tr.at("th").inner_text
44
+ when "Formed"
45
+ props[:formed] = Time.at(td_text.match(/ldst_strftime\((\d{10}), 'YMD'\)/)[1].to_i).utc
46
+ when "Rank"
47
+ props[:rank] = td_text.to_i
48
+ when "Active Members"
49
+ props[:num_members] = td_text.to_i
50
+ when "Ranking"
51
+ weekly, monthly, __garbage = td_node.inner_html.split("<br>").map(&:strip)
52
+ props[:weekly_rank] = weekly.include?("--") ? nil : weekly.match(/\d+/)[0].to_i
53
+ props[:monthly_rank] = monthly.include?("--") ? nil : monthly.match(/\d+/)[0].to_i
54
+ when "Company Slogan"
55
+ props[:slogan] = td_text
56
+ when "Active"
57
+ props[:active] = td_text
58
+ when "Recruitment"
59
+ props[:recruiting] = td_text == "Open"
60
+ when "Focus"
61
+ props[:focus] = td_node.css("img").to_a.map{|n| n.attr("title")}
62
+ when "Seeking"
63
+ props[:seeking] = td_node.css("img").to_a.map{|n| n.attr("title")}
64
+ when "Estate Profile"
65
+ name_node = td_node.at("div.txt_yellow")
66
+ if name_node
67
+ address_size, greeting = td_node.css("p").to_a.map(&:inner_text)
68
+ address, size = address_size.split(" (")
69
+ props[:housing] = {
70
+ name: name_node.inner_text,
71
+ address: address,
72
+ size: size[0...-1],
73
+ greeting: greeting
74
+ }
75
+ else
76
+ props[:housing] = nil
77
+ end
78
+ end
79
+ end
80
+ self.new(props)
81
+ rescue => e
82
+ pp e
83
+ nil
84
+ end
85
+ end
86
+ end
87
+
88
+ def members
89
+ if @members.nil?
90
+ members = {}
91
+ num_pages = (@num_members / 50.0).ceil # 50 members / page
92
+ 1.upto(num_pages) do |page_no|
93
+ dom = Lodestone.fetch("freecompany/#{@id}/member/?page=#{page_no}")
94
+ dom.css("div.player_name_area").to_a.each do |node|
95
+ cid = node.at("div.name_box a").attr("href").split("/")[-1]
96
+ fcrank = node.at("div.fc_member_status").inner_text
97
+ members[cid] = fcrank
98
+ end
99
+ end
100
+ characters = []
101
+ members.each do |cid, fcrank|
102
+ character = Character.find(cid)
103
+ character.free_company_rank = fcrank.strip
104
+ characters << character
105
+ end
106
+ @members = characters
107
+ end
108
+ @members
109
+ end
110
+
111
+ private
112
+
113
+
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,24 @@
1
+ module FFXIV
2
+ module Lodestone
3
+ class Model
4
+
5
+ class << self
6
+ def find_by_name(name, server)
7
+ self.find_by_id(self.name_to_id(name, server))
8
+ end
9
+ end
10
+
11
+ def initialize(props = {})
12
+ props.each do |name, value|
13
+ self.send("#{name}=", value) if self.respond_to?(name)
14
+ end
15
+ end
16
+
17
+ private
18
+ def self.drop_uts(str)
19
+ str[0...(str.size - 11)] # ?1234567890
20
+ end
21
+
22
+ end
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ffxiv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.1
5
+ platform: ruby
6
+ authors:
7
+ - Isjaki Kveikur
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: An unofficial FFXIV ARR toolkit for Ruby, featuring Lodestone scraper.
14
+ email:
15
+ - isjaki.xiv@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/ffxiv.rb
21
+ - lib/ffxiv/lodestone.rb
22
+ - lib/ffxiv/lodestone/character.rb
23
+ - lib/ffxiv/lodestone/free-company.rb
24
+ - lib/ffxiv/lodestone/model.rb
25
+ homepage: https://github.com/ffxiv/ffxiv
26
+ licenses:
27
+ - MIT
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.2.2
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: An unofficial FFXIV ARR toolkit for Ruby, featuring Lodestone scraper.
49
+ test_files: []