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 +7 -0
- data/lib/ffxiv.rb +6 -0
- data/lib/ffxiv/lodestone.rb +24 -0
- data/lib/ffxiv/lodestone/character.rb +112 -0
- data/lib/ffxiv/lodestone/free-company.rb +116 -0
- data/lib/ffxiv/lodestone/model.rb +24 -0
- metadata +49 -0
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,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: []
|