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:
|