ffxiv 0.9.1 → 0.9.5
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 +4 -4
- data/lib/ffxiv/lodestone.rb +20 -0
- data/lib/ffxiv/lodestone/character.rb +165 -32
- data/lib/ffxiv/lodestone/free-company.rb +46 -11
- data/lib/ffxiv/lodestone/linkshell.rb +92 -0
- metadata +30 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 20be78bbbb48a7d2a7dffb7eb33531c739d7dbbe
|
|
4
|
+
data.tar.gz: 9c3296043f94c90b6094eaf5ad680e02346f1d8b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5782cde3206205e06590e452dbb6a5baae3103758f8da63ea0cd318a46b58eee203e2dd3edf2b111a45a26c29a4c4e5465f0d14dfa5ebf412a2aa2a6ca5873d7
|
|
7
|
+
data.tar.gz: d7e2044a0c0626daa1f5a0a7f97dc7c785a0f074085b7a5612997ba7f093fc3ba426de6fb66fd4b2e902e01dcdec852bee1fb441c997502025e8bead6b3b8722
|
data/lib/ffxiv/lodestone.rb
CHANGED
|
@@ -8,17 +8,37 @@ require "open-uri"
|
|
|
8
8
|
require "lodestone/model"
|
|
9
9
|
require "lodestone/character"
|
|
10
10
|
require "lodestone/free-company"
|
|
11
|
+
require "lodestone/linkshell"
|
|
11
12
|
|
|
12
13
|
module FFXIV
|
|
13
14
|
module Lodestone
|
|
15
|
+
|
|
16
|
+
@@debug = false
|
|
17
|
+
|
|
14
18
|
class << self
|
|
19
|
+
|
|
20
|
+
def debug
|
|
21
|
+
@@debug
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def debug=(flag)
|
|
25
|
+
@@debug = flag
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def d(str)
|
|
29
|
+
puts "\033[35m[Debug]\033[0m #{str}" if @@debug
|
|
30
|
+
end
|
|
31
|
+
|
|
15
32
|
def fetch(endpoint)
|
|
16
33
|
uri = "http://na.finalfantasyxiv.com/lodestone/" + endpoint
|
|
34
|
+
d("Fetching #{uri} ......")
|
|
17
35
|
html, charset = open(uri) do |page|
|
|
18
36
|
[page.read, page.charset]
|
|
19
37
|
end
|
|
38
|
+
d(" done (size=#{html.size}, charset=#{charset})")
|
|
20
39
|
Nokogiri::HTML.parse(html, nil, charset)
|
|
21
40
|
end
|
|
41
|
+
|
|
22
42
|
end
|
|
23
43
|
end
|
|
24
44
|
end
|
|
@@ -3,16 +3,50 @@ module FFXIV
|
|
|
3
3
|
class Character < Model
|
|
4
4
|
|
|
5
5
|
attr_accessor :id, :name, :server, :thumbnail_uri, :image_uri, :race, :subrace, :gender,
|
|
6
|
-
:nameday, :guardian, :city_state, :grand_company, :grand_company_rank,
|
|
7
|
-
:
|
|
8
|
-
:num_blogs, :first_blogged, :latest_blogged, :bpd, :free_company_rank
|
|
6
|
+
:nameday, :guardian, :city_state, :grand_company, :grand_company_rank, :free_company,
|
|
7
|
+
:minions, :mounts, :end_contents, :eternal_bonding, :self_introduction, :classes,
|
|
8
|
+
:num_blogs, :first_blogged, :latest_blogged, :bpd, :free_company_rank, :linkshell_rank
|
|
9
9
|
alias :end_contents? :end_contents
|
|
10
|
+
alias :eternal_bonding? :eternal_bonding
|
|
10
11
|
|
|
11
12
|
class << self
|
|
12
13
|
|
|
14
|
+
def search(keyword, server: nil, page: 1, verbose: false)
|
|
15
|
+
dom = Lodestone.fetch("character/?q=#{URI.escape(keyword)}&worldname=#{server ? URI.escape(server) : ""}&page=#{URI.escape(page.to_s)}")
|
|
16
|
+
# Couldn't find a way to get this node with CSS...
|
|
17
|
+
characters = dom.xpath("//comment()[.=' result ']/following::table[1]/tr").map do |tr|
|
|
18
|
+
h4 = tr.at("td h4")
|
|
19
|
+
a = h4.at("a")
|
|
20
|
+
id = a.attr("href").split("/")[-1].to_i
|
|
21
|
+
if verbose
|
|
22
|
+
self.find_by_id(id)
|
|
23
|
+
else
|
|
24
|
+
self.new({
|
|
25
|
+
id: id,
|
|
26
|
+
name: a.content,
|
|
27
|
+
server: h4.at("span").content.strip[1...-1],
|
|
28
|
+
thumbnail_uri: drop_uts(tr.at("th img").attr("src"))
|
|
29
|
+
})
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
pagination = dom.at("div.current_list")
|
|
33
|
+
span_total = pagination.at("span.total")
|
|
34
|
+
raise "Character not found" unless span_total
|
|
35
|
+
total = span_total.content.to_i
|
|
36
|
+
results = {
|
|
37
|
+
show_start: pagination.at("span.show_start").content.to_i,
|
|
38
|
+
show_end: pagination.at("span.show_end").content.to_i,
|
|
39
|
+
total: total,
|
|
40
|
+
num_pages: (total / 50.0).ceil,
|
|
41
|
+
characters: characters
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
13
45
|
def name_to_id(name, server)
|
|
14
|
-
|
|
15
|
-
|
|
46
|
+
self.search(name, server: server)[:characters].each do |ch|
|
|
47
|
+
return ch.id if name == ch.name
|
|
48
|
+
end
|
|
49
|
+
nil
|
|
16
50
|
end
|
|
17
51
|
|
|
18
52
|
def find_by_id(id)
|
|
@@ -21,36 +55,57 @@ module FFXIV
|
|
|
21
55
|
|
|
22
56
|
props = {}
|
|
23
57
|
props[:id] = id
|
|
24
|
-
|
|
25
|
-
props[:
|
|
26
|
-
props[:
|
|
27
|
-
props[:
|
|
28
|
-
props[:
|
|
29
|
-
dom.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
58
|
+
ch_name = dom.at("div.player_name_txt h2 a")
|
|
59
|
+
props[:name] = ch_name.content
|
|
60
|
+
props[:server] = ch_name.next_element.content.strip[1...-1]
|
|
61
|
+
props[:thumbnail_uri] = drop_uts(dom.at("div.player_name_thumb img").attr("src"))
|
|
62
|
+
props[:image_uri] = drop_uts(dom.at("div.bg_chara_264 img").attr("src"))
|
|
63
|
+
props[:race], props[:subrace], gender = dom.at("div.chara_profile_title").content.strip.split(" / ")
|
|
64
|
+
props[:gender] = case gender
|
|
65
|
+
when "♀" then :female
|
|
66
|
+
when "♂" then :male
|
|
67
|
+
else raise "Unrecognized gender symbol: #{gender}"
|
|
40
68
|
end
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
69
|
+
|
|
70
|
+
dom.search("dl.chara_profile_box_info").each do |n|
|
|
71
|
+
n.search("dd.txt").each do |dd|
|
|
72
|
+
t = dd.next_element.content
|
|
73
|
+
case dd.content
|
|
74
|
+
when "Nameday"
|
|
75
|
+
props[:nameday] = t
|
|
76
|
+
when "Guardian"
|
|
77
|
+
props[:guardian] = t
|
|
78
|
+
when "City-state"
|
|
79
|
+
props[:city_state] = t
|
|
80
|
+
when "Grand Company"
|
|
81
|
+
props[:grand_company], props[:grand_company_rank] = t.split("/")
|
|
82
|
+
when "Free Company"
|
|
83
|
+
props[:free_company] = FreeCompany.new(name: t, server: props[:server])
|
|
84
|
+
end
|
|
85
|
+
end
|
|
45
86
|
end
|
|
87
|
+
|
|
88
|
+
# The first "minion_box" contains mounts, and they need capitalization unlike minions.
|
|
89
|
+
minion_boxes = dom.search("div.minion_box")
|
|
90
|
+
props[:mounts] = minion_boxes[0].search("a").map{|a| a.attr("title").split.map(&:capitalize).join(' ')}
|
|
91
|
+
props[:minions] = minion_boxes[1].search("a").map{|a| a.attr("title")}
|
|
92
|
+
|
|
93
|
+
# Let's assume that whoever has this mount has slained Ultima Weapon and watched the ending movie, hence is qualified for the endgame contents.
|
|
46
94
|
props[:end_contents] = props[:mounts].include?("Magitek Armor")
|
|
47
|
-
|
|
95
|
+
|
|
96
|
+
# Likewise, assume that whoever has this mount has purchased Gold or Platinum eternal bonding.
|
|
97
|
+
# Note that Standard version doesn't come with the mount, so use nil instead of false to indicate "unknown".
|
|
98
|
+
props[:eternal_bonding] = props[:mounts].include?("Ceremony Chocobo") ? true : nil
|
|
99
|
+
|
|
100
|
+
self_introduction = dom.at("div.txt_selfintroduction").inner_html.strip
|
|
101
|
+
props[:self_introduction] = self_introduction == "" ? nil : self_introduction
|
|
102
|
+
|
|
48
103
|
props[:classes] = {}
|
|
49
104
|
%w{fighter sorcerer crafter gatherer}.each do |discipline|
|
|
50
|
-
dom.
|
|
51
|
-
txt = td.
|
|
52
|
-
|
|
53
|
-
lvl = td.next_sibling().next_sibling().
|
|
105
|
+
dom.search("h4.class_#{discipline} + div.table_black_w626 td.ic_class_wh24_box").each do |td|
|
|
106
|
+
txt = td.content
|
|
107
|
+
unless txt.empty?
|
|
108
|
+
lvl = td.next_sibling().next_sibling().content.to_i
|
|
54
109
|
props[:classes][txt] = lvl == 0 ? nil : lvl
|
|
55
110
|
end
|
|
56
111
|
end
|
|
@@ -65,6 +120,13 @@ module FFXIV
|
|
|
65
120
|
|
|
66
121
|
end
|
|
67
122
|
|
|
123
|
+
def free_company(fetch = false)
|
|
124
|
+
if fetch
|
|
125
|
+
@free_company = FreeCompany.find_by_name(@free_company, @server)
|
|
126
|
+
end
|
|
127
|
+
@free_company
|
|
128
|
+
end
|
|
129
|
+
|
|
68
130
|
def num_blogs
|
|
69
131
|
init_blog; @num_blogs
|
|
70
132
|
end
|
|
@@ -81,6 +143,77 @@ module FFXIV
|
|
|
81
143
|
init_blog; @first_blogged
|
|
82
144
|
end
|
|
83
145
|
|
|
146
|
+
def blogs(load_details = false)
|
|
147
|
+
unless @blogs
|
|
148
|
+
@blogs = []
|
|
149
|
+
if num_blogs > 0
|
|
150
|
+
num_blog_pages = (num_blogs / 10.0).ceil
|
|
151
|
+
1.upto(num_blog_pages) do |page|
|
|
152
|
+
dom = Lodestone.fetch("character/#{@id}/blog?order=2&page=#{page}")
|
|
153
|
+
dom.search("section.base_body").each do |section_blog|
|
|
154
|
+
a_title = section_blog.at("a.blog_title")
|
|
155
|
+
blog = {
|
|
156
|
+
id: a_title.attr("href").split("/")[-1],
|
|
157
|
+
title: a_title.content,
|
|
158
|
+
date: Time.at(section_blog.at("script").content[/ldst_strftime\((\d{10}),/, 1].to_i),
|
|
159
|
+
num_comments: section_blog.at("span.ic_comment").content.to_i
|
|
160
|
+
}
|
|
161
|
+
if load_details
|
|
162
|
+
dom_blog = Lodestone.fetch("character/#{@id}/blog/#{blog[:id]}")
|
|
163
|
+
blog[:body] = dom_blog.at(".txt_selfintroduction").inner_html.strip
|
|
164
|
+
|
|
165
|
+
blog[:comments] = []
|
|
166
|
+
if blog[:num_comments] > 0
|
|
167
|
+
dom_blog.search("div.comment").each do |dom_comment|
|
|
168
|
+
unless dom_comment.at("div.comment_delete_box")
|
|
169
|
+
div_by = dom_comment.at("div.player_id")
|
|
170
|
+
by = nil
|
|
171
|
+
if div_by
|
|
172
|
+
a_by = div_by.at("a")
|
|
173
|
+
by = self.class.new({
|
|
174
|
+
id: a_by.attr("href").split("/")[-1],
|
|
175
|
+
name: a_by.content,
|
|
176
|
+
server: div_by.at("span").content.strip[1...-1]
|
|
177
|
+
})
|
|
178
|
+
end
|
|
179
|
+
blog[:comments] << {
|
|
180
|
+
by: by,
|
|
181
|
+
date: Time.at(dom_comment.at("script").content[/ldst_strftime\((\d{10}),/, 1].to_i),
|
|
182
|
+
body: dom_comment.at("div.balloon_body_inner").inner_html.strip
|
|
183
|
+
}
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
blog[:tags] = []
|
|
189
|
+
a_tags = dom_blog.search("div.diary_tag a")
|
|
190
|
+
if a_tags
|
|
191
|
+
a_tags.each do |a_tag|
|
|
192
|
+
blog[:tags] << a_tag.content[1...-1]
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
blog[:images] = []
|
|
197
|
+
img_thumbs = dom_blog.search("ul.thumb_list li img")
|
|
198
|
+
if img_thumbs
|
|
199
|
+
img_thumbs.each do |img_thumb|
|
|
200
|
+
unless img_thumb.attr("class") == "img_delete"
|
|
201
|
+
blog[:images] << {
|
|
202
|
+
thumbnail: img_thumb.attr("src"),
|
|
203
|
+
original: img_thumb.attr("data-origin_src")
|
|
204
|
+
}
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
@blogs << blog
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
@blogs
|
|
215
|
+
end
|
|
216
|
+
|
|
84
217
|
def thumbnail_uri
|
|
85
218
|
@thumbnail_uri + "?#{Time.now.to_i}"
|
|
86
219
|
end
|
|
@@ -94,11 +227,11 @@ module FFXIV
|
|
|
94
227
|
if @num_blogs.nil?
|
|
95
228
|
dom1 = Lodestone.fetch("character/#{@id}/blog?order=1")
|
|
96
229
|
total_node = dom1.at("div.current_list span.total")
|
|
97
|
-
@num_blogs = total_node.nil? ? 0 : total_node.
|
|
230
|
+
@num_blogs = total_node.nil? ? 0 : total_node.content.to_i
|
|
98
231
|
if @num_blogs > 0
|
|
99
232
|
dom2 = Lodestone.fetch("character/#{@id}/blog?order=2")
|
|
100
233
|
{latest_blogged: dom1, first_blogged: dom2}.each do |prop, dom|
|
|
101
|
-
txt = dom.at("h3.header_title").
|
|
234
|
+
txt = dom.at("h3.header_title").content
|
|
102
235
|
uts = txt.match(/ldst_strftime\((\d{10}), 'YMDHM'\)/)[1].to_i
|
|
103
236
|
send("#{prop}=", Time.at(uts).utc)
|
|
104
237
|
end
|
|
@@ -9,11 +9,46 @@ module FFXIV
|
|
|
9
9
|
|
|
10
10
|
class << self
|
|
11
11
|
|
|
12
|
+
def search(keyword, server: nil, page: 1, verbose: false)
|
|
13
|
+
dom = Lodestone.fetch("freecompany/?q=#{URI.escape(keyword)}&worldname=#{URI.escape(server)}&page=#{URI.escape(page.to_s)}")
|
|
14
|
+
free_companies = dom.search("table.table_elements_com_fc tr").map do |tr|
|
|
15
|
+
h4 = tr.at("td h4")
|
|
16
|
+
a = h4.at("a")
|
|
17
|
+
id = a.attr("href").split("/")[-1].to_i
|
|
18
|
+
if verbose
|
|
19
|
+
self.find_by_id(id)
|
|
20
|
+
else
|
|
21
|
+
self.new({
|
|
22
|
+
id: id,
|
|
23
|
+
name: a.content,
|
|
24
|
+
server: h4.at("span").content.strip[1...-1],
|
|
25
|
+
grand_company: tr.at("div.groundcompany_name").content
|
|
26
|
+
})
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
pagination = dom.at("div.current_list")
|
|
30
|
+
total = pagination.at("span.total").content.to_i
|
|
31
|
+
results = {
|
|
32
|
+
show_start: pagination.at("span.show_start").content.to_i,
|
|
33
|
+
show_end: pagination.at("span.show_end").content.to_i,
|
|
34
|
+
total: total,
|
|
35
|
+
num_pages: (total / 50.0).ceil,
|
|
36
|
+
free_companies: free_companies
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
12
40
|
def name_to_id(name, server)
|
|
13
41
|
dom = Lodestone.fetch("freecompany/?q=#{URI.escape(name)}&worldname=#{URI.escape(server)}")
|
|
14
42
|
dom.at("div.ic_freecompany_box a").attr("href").split("/")[-1].to_i
|
|
15
43
|
end
|
|
16
44
|
|
|
45
|
+
def name_to_id(name, server)
|
|
46
|
+
self.search(name, server: server)[:free_companies].each do |fc|
|
|
47
|
+
return fc.id if name == fc.name
|
|
48
|
+
end
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
17
52
|
def find_by_id(id)
|
|
18
53
|
begin
|
|
19
54
|
dom = Lodestone.fetch("freecompany/#{id}")
|
|
@@ -21,7 +56,7 @@ module FFXIV
|
|
|
21
56
|
props = {}
|
|
22
57
|
props[:id] = id
|
|
23
58
|
|
|
24
|
-
props[:logo_uri] = dom.
|
|
59
|
+
props[:logo_uri] = dom.search("div.ic_crest_64 img").map{|n| n.attr("src")}
|
|
25
60
|
|
|
26
61
|
gcs_node = dom.at("span.friendship_color")
|
|
27
62
|
props[:grand_company_standing] = gcs_node.inner_text[1...-1]
|
|
@@ -37,7 +72,7 @@ module FFXIV
|
|
|
37
72
|
|
|
38
73
|
props[:tag] = dom.at("td.vm").last_element_child.next_sibling.inner_text[1...-1]
|
|
39
74
|
|
|
40
|
-
dom.
|
|
75
|
+
dom.search("table.table_style2 tr").each do |tr|
|
|
41
76
|
td_node = tr.at("td")
|
|
42
77
|
td_text = td_node.inner_text.strip
|
|
43
78
|
case tr.at("th").inner_text
|
|
@@ -52,25 +87,25 @@ module FFXIV
|
|
|
52
87
|
props[:weekly_rank] = weekly.include?("--") ? nil : weekly.match(/\d+/)[0].to_i
|
|
53
88
|
props[:monthly_rank] = monthly.include?("--") ? nil : monthly.match(/\d+/)[0].to_i
|
|
54
89
|
when "Company Slogan"
|
|
55
|
-
props[:slogan] = td_text
|
|
90
|
+
props[:slogan] = td_text == "" ? nil : td_text
|
|
56
91
|
when "Active"
|
|
57
|
-
props[:active] = td_text
|
|
92
|
+
props[:active] = td_text == "Not specified" ? nil : td_text
|
|
58
93
|
when "Recruitment"
|
|
59
94
|
props[:recruiting] = td_text == "Open"
|
|
60
95
|
when "Focus"
|
|
61
|
-
props[:focus] = td_node.
|
|
96
|
+
props[:focus] = td_node.search("li:not(.icon_off) img").map{|n| n.attr("title")}
|
|
62
97
|
when "Seeking"
|
|
63
|
-
props[:seeking] = td_node.
|
|
98
|
+
props[:seeking] = td_node.search("li:not(.icon_off) img").map{|n| n.attr("title")}
|
|
64
99
|
when "Estate Profile"
|
|
65
100
|
name_node = td_node.at("div.txt_yellow")
|
|
66
101
|
if name_node
|
|
67
|
-
address_size, greeting = td_node.
|
|
102
|
+
address_size, greeting = td_node.search("p").map(&:inner_text)
|
|
68
103
|
address, size = address_size.split(" (")
|
|
69
104
|
props[:housing] = {
|
|
70
105
|
name: name_node.inner_text,
|
|
71
106
|
address: address,
|
|
72
107
|
size: size[0...-1],
|
|
73
|
-
greeting: greeting
|
|
108
|
+
greeting: greeting == "No greeting available." ? nil : greeting
|
|
74
109
|
}
|
|
75
110
|
else
|
|
76
111
|
props[:housing] = nil
|
|
@@ -91,15 +126,15 @@ module FFXIV
|
|
|
91
126
|
num_pages = (@num_members / 50.0).ceil # 50 members / page
|
|
92
127
|
1.upto(num_pages) do |page_no|
|
|
93
128
|
dom = Lodestone.fetch("freecompany/#{@id}/member/?page=#{page_no}")
|
|
94
|
-
dom.
|
|
129
|
+
dom.search("div.player_name_area").each do |node|
|
|
95
130
|
cid = node.at("div.name_box a").attr("href").split("/")[-1]
|
|
96
|
-
fcrank = node.at("div.fc_member_status").
|
|
131
|
+
fcrank = node.at("div.fc_member_status").content.strip
|
|
97
132
|
members[cid] = fcrank
|
|
98
133
|
end
|
|
99
134
|
end
|
|
100
135
|
characters = []
|
|
101
136
|
members.each do |cid, fcrank|
|
|
102
|
-
character = Character.
|
|
137
|
+
character = Character.find_by_id(cid)
|
|
103
138
|
character.free_company_rank = fcrank.strip
|
|
104
139
|
characters << character
|
|
105
140
|
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module FFXIV
|
|
2
|
+
module Lodestone
|
|
3
|
+
class Linkshell < Model
|
|
4
|
+
|
|
5
|
+
attr_accessor :id, :name, :server, :members, :num_members
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
|
|
9
|
+
def search(keyword, server: nil, page: 1, verbose: false)
|
|
10
|
+
dom = Lodestone.fetch("linkshell/?q=#{URI.escape(keyword)}&worldname=#{URI.escape(server)}&page=#{URI.escape(page.to_s)}")
|
|
11
|
+
linkshells = dom.search("table.table_elements_com_ls tr").map do |tr|
|
|
12
|
+
h4 = tr.at("th h4")
|
|
13
|
+
a = h4.at("a")
|
|
14
|
+
id = a.attr("href").split("/")[-1].to_i
|
|
15
|
+
if verbose
|
|
16
|
+
self.find_by_id(id)
|
|
17
|
+
else
|
|
18
|
+
self.new({
|
|
19
|
+
id: id,
|
|
20
|
+
name: a.content,
|
|
21
|
+
server: h4.at("span").content.strip[1...-1],
|
|
22
|
+
num_members: tr.at("td span").content.split(": ")[1].to_i
|
|
23
|
+
})
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
pagination = dom.at("div.current_list")
|
|
27
|
+
total = pagination.at("span.total").content.to_i
|
|
28
|
+
results = {
|
|
29
|
+
show_start: pagination.at("span.show_start").content.to_i,
|
|
30
|
+
show_end: pagination.at("span.show_end").content.to_i,
|
|
31
|
+
total: total,
|
|
32
|
+
num_pages: (total / 50.0).ceil,
|
|
33
|
+
linkshells: linkshells
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def name_to_id(name, server)
|
|
38
|
+
self.search(name, server: server)[:linkshells].each do |ls|
|
|
39
|
+
return ls.id if name == ls.name
|
|
40
|
+
end
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def find_by_id(id)
|
|
45
|
+
begin
|
|
46
|
+
dom = Lodestone.fetch("linkshell/#{id}")
|
|
47
|
+
props = {}
|
|
48
|
+
props[:id] = id
|
|
49
|
+
buff = dom.at("div.contents_header h2").content.match(/^(.+)\s\((.+)\)$/)
|
|
50
|
+
props[:name] = buff[1]
|
|
51
|
+
props[:server] = buff[2]
|
|
52
|
+
props[:num_members] = dom.at("h3.ic_silver").content.match(/\s\((\d+)\s/)[1].to_i
|
|
53
|
+
self.new(props)
|
|
54
|
+
rescue => e
|
|
55
|
+
pp e
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def members
|
|
62
|
+
if @members.nil?
|
|
63
|
+
members = {}
|
|
64
|
+
num_pages = (@num_members / 50.0).ceil # 50 members / page
|
|
65
|
+
1.upto(num_pages) do |page_no|
|
|
66
|
+
dom = Lodestone.fetch("linkshell/#{@id}?page=#{page_no}")
|
|
67
|
+
dom.search("div.player_name_area").each do |node|
|
|
68
|
+
cid = node.at("div.name_box a").attr("href").split("/")[-1]
|
|
69
|
+
if node.at("span.ic_master")
|
|
70
|
+
lsrank = :master
|
|
71
|
+
elsif node.at("span.ic_leader")
|
|
72
|
+
lsrank = :leader
|
|
73
|
+
else
|
|
74
|
+
lsrank = :member
|
|
75
|
+
end
|
|
76
|
+
members[cid] = lsrank
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
characters = []
|
|
80
|
+
members.each do |cid, lsrank|
|
|
81
|
+
character = Character.find_by_id(cid)
|
|
82
|
+
character.linkshell_rank = lsrank
|
|
83
|
+
characters << character
|
|
84
|
+
end
|
|
85
|
+
@members = characters
|
|
86
|
+
end
|
|
87
|
+
@members
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
metadata
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ffxiv
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Syro Bonkus
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
13
|
-
|
|
11
|
+
date: 2015-03-03 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: nokogiri
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 1.5.0
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 1.7.0
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 1.5.0
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 1.7.0
|
|
33
|
+
description: An unofficial Final Fantasy XIV / A Realm Reborn toolkit for Ruby, featuring
|
|
34
|
+
Lodestone scraper.
|
|
14
35
|
email:
|
|
15
|
-
-
|
|
36
|
+
- sb.ffxiv@gmail.com
|
|
16
37
|
executables: []
|
|
17
38
|
extensions: []
|
|
18
39
|
extra_rdoc_files: []
|
|
@@ -21,6 +42,7 @@ files:
|
|
|
21
42
|
- lib/ffxiv/lodestone.rb
|
|
22
43
|
- lib/ffxiv/lodestone/character.rb
|
|
23
44
|
- lib/ffxiv/lodestone/free-company.rb
|
|
45
|
+
- lib/ffxiv/lodestone/linkshell.rb
|
|
24
46
|
- lib/ffxiv/lodestone/model.rb
|
|
25
47
|
homepage: https://github.com/ffxiv/ffxiv
|
|
26
48
|
licenses:
|
|
@@ -42,8 +64,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
42
64
|
version: '0'
|
|
43
65
|
requirements: []
|
|
44
66
|
rubyforge_project:
|
|
45
|
-
rubygems_version: 2.
|
|
67
|
+
rubygems_version: 2.4.5
|
|
46
68
|
signing_key:
|
|
47
69
|
specification_version: 4
|
|
48
70
|
summary: An unofficial FFXIV ARR toolkit for Ruby, featuring Lodestone scraper.
|
|
49
71
|
test_files: []
|
|
72
|
+
has_rdoc:
|