From 6820aff913f99c3f6e03099f042893e082dae803 Mon Sep 17 00:00:00 2001 From: Kyle Clemens Date: Sun, 2 Sep 2018 15:12:52 -0400 Subject: [PATCH] chore: initial commit --- .gitignore | 3 + Cargo.toml | 28 + LICENSE | 21 + README.md | 3 + html/Characters/A.html | 1401 ++++++++++++++++++++++++++++++ html/Characters/Duvivi.html | 1541 +++++++++++++++++++++++++++++++++ html/Characters/Sakae.html | 1485 +++++++++++++++++++++++++++++++ html/FCs/Cobra.html | 1215 ++++++++++++++++++++++++++ html/FCs/Rose.html | 1253 +++++++++++++++++++++++++++ html/Searches/Character.html | 1583 ++++++++++++++++++++++++++++++++++ schemas/FreeCompany.md | 28 + src/error.rs | 32 + src/lib.rs | 9 + src/logic.rs | 12 + src/logic/character.rs | 106 +++ src/logic/free_company.rs | 155 ++++ src/models.rs | 27 + src/models/character.rs | 77 ++ src/models/free_company.rs | 44 + 19 files changed, 9023 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 html/Characters/A.html create mode 100644 html/Characters/Duvivi.html create mode 100644 html/Characters/Sakae.html create mode 100644 html/FCs/Cobra.html create mode 100644 html/FCs/Rose.html create mode 100644 html/Searches/Character.html create mode 100644 schemas/FreeCompany.md create mode 100644 src/error.rs create mode 100644 src/lib.rs create mode 100644 src/logic.rs create mode 100644 src/logic/character.rs create mode 100644 src/logic/free_company.rs create mode 100644 src/models.rs create mode 100644 src/models/character.rs create mode 100644 src/models/free_company.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a1e44cc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,28 @@ +cargo-features = ["edition"] + +[package] +name = "lodestone_parser" +version = "0.1.0" +authors = ["Kyle Clemens "] + +edition = "2018" + +[dependencies] +cssparser = "0.23" +failure = "0.1" +lazy_static = "1" +scraper = "0.7" +serde = "1" +serde_derive = "1" +serde_json = "1" +url = "1" +url_serde = "0.2" + +[dependencies.chrono] +version = "0.4" +features = ["serde"] + +[dependencies.ffxiv_types] +version = "0.2" +default-features = false +features = ["worlds", "with_serde"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c59bec4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Kyle Clemens + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8492244 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# lodestone_parser + +It parses Lodestone HTML. It's also nowhere near done. diff --git a/html/Characters/A.html b/html/Characters/A.html new file mode 100644 index 0000000..69fb375 --- /dev/null +++ b/html/Characters/A.html @@ -0,0 +1,1401 @@ + + + +A' A' | FINAL FANTASY XIV, The Lodestone + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+ + +
+ + + + + + +
+ + + +
+ +
+ + + + + + + + + + + + + + +
+ + + + +

Character

+ + + + + + + +
+ +
+ + + +
+

Character

+ + + +
+ +
+ +
+
+ +

A' A'

+ +

Anima

+
+
+
+ + + + +
+ +
+ +

You have no connection with this character.

+ + + + + +
Follower Requests
+

Before this character can be followed, you must first submit a follower request.
Do you wish to proceed?

+ + + + +
+ +
+ + + + + + +
+ + +

Profile

+ + +
+
+
+
+ +
+

Race/Clan/Gender

+

Au Ra
Raen / ♀

+
+
+ +
+ +
+

Nameday

+

32st Sun of the 6th Umbral Moon

+

Guardian

+

Azeyma, the Warden

+
+
+ +
+ +
+

City-state

+

Limsa Lominsa

+
+
+ + + + + + + + + + + +
+ +
+
+ + + + +
+
+
+ + +
+ + +
+ + + +
+ + +
+ +
+
+

LEVEL 4

+
+ +
+ +
+
+
+ +
+ + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ +
+ + + + +
+ + +
+ + + +
+ + +
+ +
+ + + + +
+ + +
+ + + +
+ +
+ + + +
+ + +
+ +
+ + + + +
+ + +
+ + + +
+ + +
+ +
+ + + + +
+ + +
+ +
+ +
+ + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ +
+ + + +
+ +
+ +
+
+
+ + + + +
+
+
    +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
+
+
+ + +
+
+
    +
  • -
  • +
  • -
  • +
  • 4
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • 10
  • +
  • -
  • +
+
+
+ +
+
+
    +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
+
+
+ +
+
+
    +
  • -
  • +
  • -
  • +
  • -
  • +
+
+
+ +
+
+ + + +
+

Character Profile

+ +
+ + +
+ + - + +
+ + + + + + +
+ + + + +
+ + +
+ +

Attributes

Attributes

Strength17
Dexterity25
Vitality20
Intelligence14
Mind19

Offensive Properties

Critical Hit Rate62
Determination25
Direct Hit Rate62

Defensive Properties

Defense31
Magic Defense64

Physical Properties

Attack Power25
Skill Speed62

Mental Properties

Attack Magic Potency14
Healing Magic Potency19
Spell Speed62

Role

Tenacity62
Piety16

LEVEL 4

  • HP

    87
  • MP

    53
  • TP

    1000

DoW/DoM

Tank

  • -
    Gladiator
    - / -
  • -
    Marauder
    - / -
  • -
    Dark Knight
    - / -

Melee DPS

  • -
    Pugilist
    - / -
  • -
    Lancer
    - / -
  • 4
    Rogue
    1037 / 1700
  • -
    Samurai
    - / -

Healer

  • -
    Conjurer
    - / -
  • -
    Scholar
    - / -
  • -
    Astrologian
    - / -

Physical Ranged DPS

  • -
    Archer
    - / -
  • -
    Machinist
    - / -

Magical Ranged DPS

  • -
    Thaumaturge
    - / -
  • 10
    Arcanist
    5733 / 11800
  • -
    Red Mage
    - / -

DoH/DoL

Disciples of the Hand

  • -
    Carpenter
    - / -
  • -
    Blacksmith
    - / -
  • -
    Armorer
    - / -
  • -
    Goldsmith
    - / -
  • -
    Leatherworker
    - / -
  • -
    Weaver
    - / -
  • -
    Alchemist
    - / -
  • -
    Culinarian
    - / -

Disciples of the Land

  • -
    Miner
    - / -
  • -
    Botanist
    - / -
  • -
    Fisher
    - / -

Mounts

Minions

+
+
+ +
+ + + + +
+ ForumsMog StationOfficial Blog +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + +
+ + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + + + + +
+ + + +
+

Community Wall

+ +
+
+

Recent Activity

+ +
+

+ Filter which items are to be displayed below.
+ + * Notifications for standings updates are shared across all Worlds.
+ * Notifications for PvP team formations are shared for all languages.
+ * Notifications for free company formations are shared for all languages. +
+

+ + +
+
Sort by
Data Center / World
Primary language
Displaying
+
+ + +
+
+ + + + + +
+ + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ + +
+ +

Cookie Policy

+

This website uses cookies. If you do not wish us to set cookies on your device, please do not use the website. Please read the Square Enix cookies policy for more information. Your use of the website is also subject to the terms in the Square Enix website terms of use and privacy policy and by using the website you are accepting those terms. The Square Enix terms of use, privacy policy and cookies policy can also be found through links at the bottom of the page.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/html/Characters/Duvivi.html b/html/Characters/Duvivi.html new file mode 100644 index 0000000..e9b59cb --- /dev/null +++ b/html/Characters/Duvivi.html @@ -0,0 +1,1541 @@ + + + +Duvivi Duvi | FINAL FANTASY XIV, The Lodestone + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+ + +
+ + + + + + +
+ + + +
+ +
+ + + + + + + + + + + + + + +
+ + + + +

Character

+ + + + + + + +
+ +
+ + + +
+

Character

+ + + +
+ +
+ +
+
+ +

Duvivi Duvi

+

Domitrix

+

Adamantoise

+
+
+
+ + + + +
+ +
+ +

You have no connection with this character.

+ + + + + +
Follower Requests
+

Before this character can be followed, you must first submit a follower request.
Do you wish to proceed?

+ + + + +
+ +
+ + + + + + +
+ + +

Profile

+ + +
+
+
+
+ +
+

Race/Clan/Gender

+

Lalafell
Plainsfolk / ♀

+
+
+ +
+ +
+

Nameday

+

18th Sun of the 2nd Umbral Moon

+

Guardian

+

Rhalgr, the Destroyer

+
+
+ +
+ +
+

City-state

+

Gridania

+
+
+ + + +
+ +
+

Grand Company

+

Order of the Twin Adder / First Serpent Lieutenant

+
+
+ + + +
+
+
+ + + + + + + + + +
+
+
+
+

Free Company

+

Order of Wild Rose

+
+
+
+ + + + + + + +
+ +
+
+ + + + +
+
+
+ + +
+ + +
+ + + +
+ + +
+ +
+
+

LEVEL 67

+
+ +
+ +
+
+
+ +
+ + +
+ + +
+ + + + +
+ + + + + + +
+ + +
+ + + +
+ + +
+ + + + +
+ + + + + + +
+ + +
+ + + +
+ + +
+ + + + +
+ + + + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + + +
+ + + + + + +
+ + +
+ + + +
+ + +
+ + + + +
+ + + + + + +
+ + +
+ +
+ +
+ + +
+ +
+ + + +
+ + +
+ + + + +
+ + + + + +
+ + +
+ + + +
+ + +
+ + + + +
+ + + + + +
+ + +
+ + + +
+ + +
+ + + + +
+ + + + + +
+ + +
+ + + +
+ + +
+ + + + +
+ + + + + +
+ + +
+ + + +
+ + +
+ + + + +
+ + + + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ +
+
+
+ + + + +
+
+
    +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
+
+
+ + +
+
+
    +
  • 60
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 60
  • +
  • 70
  • +
  • 70
  • +
  • 67
  • +
+
+
+ +
+
+
    +
  • 34
  • +
  • 36
  • +
  • 28
  • +
  • 34
  • +
  • 26
  • +
  • 39
  • +
  • 20
  • +
  • 13
  • +
+
+
+ +
+
+
    +
  • 60
  • +
  • 24
  • +
  • 11
  • +
+
+
+ +
+
+ + + +
+

Character Profile

+ +
+ + +
+ + - + +
+ + + + + + +
+ + + + +
+ + +
+ +

Attributes

Attributes

Strength146
Dexterity284
Vitality1216
Intelligence1716
Mind294

Offensive Properties

Critical Hit Rate878
Determination664
Direct Hit Rate809

Defensive Properties

Defense1077
Magic Defense1882

Physical Properties

Attack Power146
Skill Speed361

Mental Properties

Attack Magic Potency1716
Healing Magic Potency294
Spell Speed836

Role

Tenacity361
Piety268

LEVEL 67

  • HP

    23562
  • MP

    12672
  • TP

    1000

DoW/DoM

Tank

  • 70
    Paladin
    -- / --
  • 70
    Warrior
    -- / --
  • 70
    Dark Knight
    -- / --

Melee DPS

  • 60
    Monk
    18040 / 4470000
  • 70
    Dragoon
    -- / --
  • 70
    Ninja
    -- / --
  • 70
    Samurai
    -- / --

Healer

  • 70
    White Mage
    -- / --
  • 70
    Scholar
    -- / --
  • 70
    Astrologian
    -- / --

Physical Ranged DPS

  • 70
    Bard
    -- / --
  • 60
    Machinist
    174445 / 4470000

Magical Ranged DPS

  • 70
    Black Mage
    -- / --
  • 70
    Summoner
    -- / --
  • 67
    Red Mage
    3255278 / 8575000

DoH/DoL

Disciples of the Hand

  • 34
    Carpenter
    157003 / 217900
  • 36
    Blacksmith
    203221 / 249900
  • 28
    Armorer
    41954 / 140200
  • 34
    Goldsmith
    29799 / 217900
  • 26
    Leatherworker
    5682 / 109800
  • 39
    Weaver
    75282 / 304900
  • 20
    Alchemist
    44869 / 56600
  • 13
    Culinarian
    17114 / 23700

Disciples of the Land

  • 60
    Miner
    133774 / 4470000
  • 24
    Botanist
    70001 / 87100
  • 11
    Fisher
    700 / 15600

Mounts

Minions

+
+
+ +
+ + + + +
+ ForumsMog StationOfficial Blog +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + +
+ + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + + + + +
+ + + +
+

Community Wall

+ +
+
+

Recent Activity

+ +
+

+ Filter which items are to be displayed below.
+ + * Notifications for standings updates are shared across all Worlds.
+ * Notifications for PvP team formations are shared for all languages.
+ * Notifications for free company formations are shared for all languages. +
+

+ + +
+
Sort by
Data Center / World
Primary language
Displaying
+
+ + +
+
+ + + + + +
+ + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ + +
+ +

Cookie Policy

+

This website uses cookies. If you do not wish us to set cookies on your device, please do not use the website. Please read the Square Enix cookies policy for more information. Your use of the website is also subject to the terms in the Square Enix website terms of use and privacy policy and by using the website you are accepting those terms. The Square Enix terms of use, privacy policy and cookies policy can also be found through links at the bottom of the page.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/html/Characters/Sakae.html b/html/Characters/Sakae.html new file mode 100644 index 0000000..2b45e5e --- /dev/null +++ b/html/Characters/Sakae.html @@ -0,0 +1,1485 @@ + + + +Sakae Blockade | FINAL FANTASY XIV, The Lodestone + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+ + +
+ + + + + + +
+ + + +
+ +
+ + + + + + + + + + + + + + +
+ + + + +

Character

+ + + + + + + +
+ +
+ + + +
+

Character

+ + + +
+ +
+ +
+
+ +

Sakae Blockade

+ +

Ridill

+
+
+
+ + + + +
+ +
+ +

You have no connection with this character.

+ + + + + +
Follower Requests
+

Follow this character?

+ + + + +
+ +
+ + + + + + +
+ + +

Profile

+ + +
+
+
+
+ +
+

Race/Clan/Gender

+

Lalafell
Plainsfolk / ♂

+
+
+ +
+ +
+

Nameday

+

28th Sun of the 5th Astral Moon

+

Guardian

+

Menphina, the Lover

+
+
+ +
+ +
+

City-state

+

Gridania

+
+
+ + + +
+ +
+

Grand Company

+

Order of the Twin Adder / First Serpent Lieutenant

+
+
+ + + +
+
+
+ + + + + + + + + +
+
+
+
+

Free Company

+

Ain Soph Aur

+
+
+
+ + + + + + + +
+ +
+
+ + + + +
+
+
+ + +
+ + +
+ +
+ + + + +
+ + +
+ +
+
+

LEVEL 70

+
+ +
+ +
+
+
+ +
+ + +
+ + +
+ +
+ + + + +
+ + +
+ + + +
+ + +
+ +
+ + + + +
+ + +
+ + + +
+ + +
+ +
+ + + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ +
+ + + + +
+ + +
+ + + +
+ + +
+ +
+ + + + +
+ + +
+ +
+ +
+ + +
+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ +
+
+
+ + + + +
+
+
    +
  • 50
  • +
  • 50
  • +
  • 30
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
+
+
+ + +
+
+
    +
  • 50
  • +
  • 50
  • +
  • 68
  • +
  • -
  • +
  • 70
  • +
  • 30
  • +
  • 60
  • +
  • 70
  • +
  • -
  • +
+
+
+ +
+
+
    +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
  • 70
  • +
+
+
+ +
+
+
    +
  • 70
  • +
  • 70
  • +
  • 70
  • +
+
+
+ +
+
+ + + +
+

Character Profile

+ +
+ + +
+ + + +
+ + + + + + +
+ + + + +
+ + +
+ +

Attributes

Attributes

Strength159
Dexterity309
Vitality2134
Intelligence308
Mind2870

Offensive Properties

Critical Hit Rate1002
Determination980
Direct Hit Rate364

Defensive Properties

Defense2177
Magic Defense3805

Physical Properties

Attack Power159
Skill Speed364

Mental Properties

Attack Magic Potency2870
Healing Magic Potency2870
Spell Speed1339

Role

Tenacity364
Piety2299

LEVEL 70

  • HP

    43383
  • MP

    21761
  • TP

    1000

DoW/DoM

Tank

  • 50
    Paladin
    0 / 864000
  • 50
    Warrior
    0 / 864000
  • 30
    Dark Knight
    2504 / 162500

Melee DPS

  • 50
    Monk
    0 / 864000
  • 50
    Dragoon
    0 / 864000
  • 68
    Ninja
    3223217 / 9593000
  • -
    Samurai
    - / -

Healer

  • 70
    White Mage
    -- / --
  • 70
    Scholar
    -- / --
  • 70
    Astrologian
    -- / --

Physical Ranged DPS

  • 70
    Bard
    -- / --
  • 30
    Machinist
    0 / 162500

Magical Ranged DPS

  • 60
    Black Mage
    0 / 4470000
  • 70
    Summoner
    -- / --
  • -
    Red Mage
    - / -

DoH/DoL

Disciples of the Hand

  • 70
    Carpenter
    -- / --
  • 70
    Blacksmith
    -- / --
  • 70
    Armorer
    -- / --
  • 70
    Goldsmith
    -- / --
  • 70
    Leatherworker
    -- / --
  • 70
    Weaver
    -- / --
  • 70
    Alchemist
    -- / --
  • 70
    Culinarian
    -- / --

Disciples of the Land

  • 70
    Miner
    -- / --
  • 70
    Botanist
    -- / --
  • 70
    Fisher
    -- / --

Mounts

Minions

+
+
+ +
+ + + + +
+ ForumsMog StationOfficial Blog +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + +
+ + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + + + + +
+ + + +
+

Community Wall

+ +
+
+

Recent Activity

+ +
+

+ Filter which items are to be displayed below.
+ + * Notifications for standings updates are shared across all Worlds.
+ * Notifications for PvP team formations are shared for all languages.
+ * Notifications for free company formations are shared for all languages. +
+

+ + +
+
Sort by
Data Center / World
Primary language
Displaying
+
+ + +
+
+ + + + + +
+ + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ + +
+ +

Cookie Policy

+

This website uses cookies. If you do not wish us to set cookies on your device, please do not use the website. Please read the Square Enix cookies policy for more information. Your use of the website is also subject to the terms in the Square Enix website terms of use and privacy policy and by using the website you are accepting those terms. The Square Enix terms of use, privacy policy and cookies policy can also be found through links at the bottom of the page.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/html/FCs/Cobra.html b/html/FCs/Cobra.html new file mode 100644 index 0000000..1229176 --- /dev/null +++ b/html/FCs/Cobra.html @@ -0,0 +1,1215 @@ + + + +! - Cobra - ! | FINAL FANTASY XIV, The Lodestone + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+ + +
+ + + + + + +
+ + + +
+ +
+ + + + + + + + + + + + + + +
+ + + + +

Free Company

+ + + + + + +
+ +
+ +
+

Company Information

+
+ +
+
+ +
+ + + + + + + + + +
+
+
+
+

Immortal Flames <Allied>

+

! - Cobra - !

+

+ + Famfrit + +

+
+
+
+ + +

Company Slogan

+

A ruthless, terrorist organization determined to rule the world.
~>`)~~~~~

+ +

Free Company Name «Company Tag»

+ +

! - Cobra - !

+

«Cobra»

+ + +

Formed

+

+ +- + + +

+ + +

Active Members

+

9

+ + +

Rank

+

8

+ + +

Reputation

+
+
+ +
+
+

Maelstrom

+

Neutral

+
+
+
+
+
+
+
+ +
+
+

Order of the Twin Adder

+

Neutral

+
+
+
+
+
+
+
+ +
+
+

Immortal Flames

+

Allied

+
+
+
+
+
+ + +

Ranking

+ + + + + + + +
Weekly Rank:-- (previous week)
Monthly Rank:-- (previous month)
+

* Overall free company standings on your World.

+ + +

Estate Profile

+ +

No Estate or Plot

+ +
+ +
+

Focus

+ +

Active

+

+ + Not specified + +

+

Recruitment

+

+ + Closed + +

+

Focus

+ +
    + +
  • +
    +

    Role-playing

    +
  • + +
  • +
    +

    Leveling

    +
  • + +
  • +
    +

    Casual

    +
  • + +
  • +
    +

    Hardcore

    +
  • + +
  • +
    +

    Dungeons

    +
  • + +
  • +
    +

    Guildhests

    +
  • + +
  • +
    +

    Trials

    +
  • + +
  • +
    +

    Raids

    +
  • + +
  • +
    +

    PvP

    +
  • + +
+ +

Seeking

+ +

Not specified

+ + +
+
+
+
+ +
+ + + + +
+ ForumsMog StationOfficial Blog +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + +
+ + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + + + + +
+ + + +
+

Community Wall

+ +
+
+

Recent Activity

+ +
+

+ Filter which items are to be displayed below.
+ + * Notifications for standings updates are shared across all Worlds.
+ * Notifications for PvP team formations are shared for all languages.
+ * Notifications for free company formations are shared for all languages. +
+

+ + +
+
Sort by
Data Center / World
Primary language
Displaying
+
+ + +
+
+ + + + + +
+ + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ + +
+ +

Cookie Policy

+

This website uses cookies. If you do not wish us to set cookies on your device, please do not use the website. Please read the Square Enix cookies policy for more information. Your use of the website is also subject to the terms in the Square Enix website terms of use and privacy policy and by using the website you are accepting those terms. The Square Enix terms of use, privacy policy and cookies policy can also be found through links at the bottom of the page.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/html/FCs/Rose.html b/html/FCs/Rose.html new file mode 100644 index 0000000..6a06ec7 --- /dev/null +++ b/html/FCs/Rose.html @@ -0,0 +1,1253 @@ + + + +Order of Wild Rose | FINAL FANTASY XIV, The Lodestone + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+ + +
+ + + + + + +
+ + + +
+ +
+ + + + + + + + + + + + + + +
+ + + + +

Free Company

+ + + + + + +
+ +
+ +
+

Company Information

+
+ +
+
+ +
+ + + + + + + + + +
+
+
+
+

Maelstrom <Allied>

+

Order of Wild Rose

+

+ + Adamantoise + +

+
+
+
+ + +

Company Slogan

+

A Rainbow Coalition of Puns

+ +

Free Company Name «Company Tag»

+ +

Order of Wild Rose

+

«Rose»

+ + +

Formed

+

+ +- + + +

+ + +

Active Members

+

93

+ + +

Rank

+

8

+ + +

Reputation

+
+
+ +
+
+

Maelstrom

+

Allied

+
+
+
+
+
+
+
+ +
+
+

Order of the Twin Adder

+

Neutral

+
+
+
+
+
+
+
+ +
+
+

Immortal Flames

+

Neutral

+
+
+
+
+
+ + +

Ranking

+ + + + + + + +
Weekly Rank:189 (previous week)
Monthly Rank:96 (previous month)
+

* Overall free company standings on your World.

+ + +

Estate Profile

+ + + +

The Garden

+ + +

Address

+

Plot 5, 14 Ward, Mist (Large)

+ +

Greeting

+ +

Where the Roses grow wild and free.

+ + +
+ +
+

Focus

+ +

Active

+

+ + Always + +

+

Recruitment

+

+ + Open + +

+

Focus

+ +
    + +
  • +
    +

    Role-playing

    +
  • + +
  • +
    +

    Leveling

    +
  • + +
  • +
    +

    Casual

    +
  • + +
  • +
    +

    Hardcore

    +
  • + +
  • +
    +

    Dungeons

    +
  • + +
  • +
    +

    Guildhests

    +
  • + +
  • +
    +

    Trials

    +
  • + +
  • +
    +

    Raids

    +
  • + +
  • +
    +

    PvP

    +
  • + +
+ +

Seeking

+ +
    + +
  • +
    +

    Tank

    +
  • + +
  • +
    +

    Healer

    +
  • + +
  • +
    +

    DPS

    +
  • + +
  • +
    +

    Crafter

    +
  • + +
  • +
    +

    Gatherer

    +
  • + +
+ + +
+
+
+
+ +
+ + + + +
+ ForumsMog StationOfficial Blog +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + +
+ + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + + + + +
+ + + +
+

Community Wall

+ +
+
+

Recent Activity

+ +
+

+ Filter which items are to be displayed below.
+ + * Notifications for standings updates are shared across all Worlds.
+ * Notifications for PvP team formations are shared for all languages.
+ * Notifications for free company formations are shared for all languages. +
+

+ + +
+
Sort by
Data Center / World
Primary language
Displaying
+
+ + +
+
+ + + + + +
+ + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ + +
+ +

Cookie Policy

+

This website uses cookies. If you do not wish us to set cookies on your device, please do not use the website. Please read the Square Enix cookies policy for more information. Your use of the website is also subject to the terms in the Square Enix website terms of use and privacy policy and by using the website you are accepting those terms. The Square Enix terms of use, privacy policy and cookies policy can also be found through links at the bottom of the page.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/html/Searches/Character.html b/html/Searches/Character.html new file mode 100644 index 0000000..7675377 --- /dev/null +++ b/html/Searches/Character.html @@ -0,0 +1,1583 @@ + + + +Characters | FINAL FANTASY XIV, The Lodestone + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+ + +
+ + + + + + +
+ + + +
+ +
+ + + + + + + + + + + + + + +
+ + + + +

Character

+ + + +
+ +
+ +
+

Character Search

+

Search Results

+ +
+ +
+
+ +

Data Center / World

+
+ +
+

Class/Job

+
+ +
+

Race / Clan:

+
+ +
+

Grand Company

+
+

Primary language

+
    + +
  • + + +
  • + +
  • + + +
  • + +
  • + + +
  • + +
  • + + +
  • + +
+
+ +
+
+ + + + +
Character: a| Data Center/World : All| Class/Job: All| Race / Clan: All| Grand Company: Maelstrom, Order of the Twin Adder, Immortal Flames, No Affiliation| Language(s): Japanese, English, German, French
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
1000 Total
+
+
+ + + + +

The maximum number of displayable items has been reached.

+ + +
    + +
  • +
  • + + +
  • Page 1 of 20
  • + + +
  • +
  • + +
+ + + + +
A' A'

A' A'

Anima

  • 4
JA
+ + +
A' A'

A' A'

Valefor

  • 1
JA
+ + +
A' A'

A' A'

Ridill

  • 1
JA
+ + +
A' A'

A' A'

Alexander

  • 65
JA
+ + +
A' A'

A' A'

Ramuh

  • 2
JA
+ + +
A' A'

A' A'

Leviathan

  • 14
EN
+ + +
A' A'

A' A'

Ixion

  • 1
JA
+ + +
A' A'

A' A'

Masamune

  • 1
JA
+ + +
A' A'

A' A'

Unicorn

  • 1
JA
+ + +
A' A'

A' A'

Mandragora

  • 1
JA
+ + +
A' A'

A' A'

Chocobo

  • 25
JA
+ + +
A' A'

A' A'

Gilgamesh

  • 20
JA
Zako's FanClub
+ + +
A' A'

A' A'

Siren

  • 1
JA
+ + +
A' A'

A' A'

Hyperion

  • 1
JA/EN/DE/FR
+ + +
A' A'

A' A'

Adamantoise

  • 1
EN
+ + +
A' A'

A' A'

Odin

  • 1
EN
+ + +
A' A'

A' A'

Bahamut

  • 17
JA
+ + +
A' A'

A' A'

Phoenix

  • 60
JA
Edart
+ + +
A' A'

A' A'

Gungnir

  • 47
JA
+ + +
A' A's

A' A's

Unicorn

  • 3
JA
+ + +
A' Ace

A' Ace

Famfrit

  • 13
EN
+ + +
A' Ace

A' Ace

Ifrit

  • 70
JA
crow
+ + +
A' Aeglar

A' Aeglar

Jenova

  • 37
EN
+ + +
A' Akumatan

A' Akumatan

Atomos

  • 1
JA
JBlack Microwave
+ + +
A' Alfred

A' Alfred

Masamune

  • 60
JA
+ + +
A' Alia'

A' Alia'

Brynhildr

  • 13
EN
+ + +
A' Alice

A' Alice

Chocobo

  • 50
JA
+ + +
A' Alicee

A' Alicee

Lich

  • 1
EN
+ + +
A' Aliyah

A' Aliyah

Ridill

  • 12
JA
+ + +
A' Antmod

A' Antmod

Masamune

  • 70
EN
Old Man
+ + +
A' Ardu

A' Ardu

Brynhildr

  • 32
EN
+ + +
A' Aron

A' Aron

Phoenix

  • 55
FR
+ + +
A' Aron

A' Aron

Famfrit

  • 18
EN
Fractured but Whole
+ + +
A' Asasimo

A' Asasimo

Yojimbo

  • 50
JA
+ + +
A' Aulia

A' Aulia

Brynhildr

  • 1
EN
+ + +
A' Ayukawa

A' Ayukawa

Bahamut

  • 1
JA
+ + +
A' Ayukawa

A' Ayukawa

Alexander

  • 1
JA
+ + +
A' B'

A' B'

Lich

  • 1
EN
+ + +
A' B'

A' B'

Hyperion

  • 1
EN
+ + +
A' B'

A' B'

Tonberry

  • 1
JA/EN/DE/FR
+ + +
A' Babboune

A' Babboune

Cerberus

  • 62
EN/FR
+ + +
A' Balloon

A' Balloon

Omega

  • 22
EN
+ + +
A' Bara

A' Bara

Durandal

  • 1
JA
+ + +
A' Bard

A' Bard

Kujata

  • 18
EN
+ + +
A' Bard

A' Bard

Tonberry

  • 54
EN
Imperial Blood
+ + +
A' Bear

A' Bear

Faerie

  • 3
EN
+ + +
A' Bear

A' Bear

Asura

  • 70
JA
+ + +
A' Beaver

A' Beaver

Malboro

  • 9
EN
+ + +
A' Bird

A' Bird

Adamantoise

  • 1
EN
+ + +
A' Blinkin

A' Blinkin

Faerie

  • 22
EN
+ + + +
    + +
  • +
  • + + +
  • Page 1 of 20
  • + + +
  • +
  • + +
+ + + + + + +
+
+ +
+ + + + +
+ ForumsMog StationOfficial Blog +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + +
+ + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + + + + +
+ + + +
+

Community Wall

+ +
+
+

Recent Activity

+ +
+

+ Filter which items are to be displayed below.
+ + * Notifications for standings updates are shared across all Worlds.
+ * Notifications for PvP team formations are shared for all languages.
+ * Notifications for free company formations are shared for all languages. +
+

+ + +
+
Sort by
Data Center / World
Primary language
Displaying
+
+ + +
+
+ + + + + +
+ + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ + +
+ +

Cookie Policy

+

This website uses cookies. If you do not wish us to set cookies on your device, please do not use the website. Please read the Square Enix cookies policy for more information. Your use of the website is also subject to the terms in the Square Enix website terms of use and privacy policy and by using the website you are accepting those terms. The Square Enix terms of use, privacy policy and cookies policy can also be found through links at the bottom of the page.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/schemas/FreeCompany.md b/schemas/FreeCompany.md new file mode 100644 index 0000000..0630158 --- /dev/null +++ b/schemas/FreeCompany.md @@ -0,0 +1,28 @@ +# FreeCompany + +|Key|Value|Description| +|---|---|---| +|`name`|`String`|The name of the Free Company.| +|`world`|`String`|The world the Free Company is on.| +|`slogan`|`String`|The Free Company's slogan.| +|`crest`|`Array` of `String`|The image URLs that are layered to created the Free Company crest.| +|`active_members`|`u16`|The amount of active members. +|`rank`|`u8`|The Free Company's rank ([1,8]).| +|`pvp_rankings`|`PvpRankings`|The Free Company's PvP rankings.| +|`formed`|`DateTime` (UTC, RFC3339 formatted)|The date and time at which the Free Company was created.| +|`estate`|`Estate?`|The Free Company's estate.| + +## PvpRankings + +|Key|Value|Description| +|---|---|---| +|`weekly`|`u64?`|The weekly rank or `null` if unranked.| +|`monthly`|`u64?`|The monthly rank or `null` if unranked.| + +## Estate + +|Key|Value|Description| +|---|---|---| +|`name`|`String`|The name of the estate.| +|`address`|`String`|The estate's address.| +|`greeting`|`String`|The greeting set on the estate.| diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..c87e29f --- /dev/null +++ b/src/error.rs @@ -0,0 +1,32 @@ +use failure::Fail; + +pub type Result = std::result::Result; + +#[derive(Debug, Fail)] +pub enum Error { + #[fail(display = "couldn't find expected element on the Lodestone: {}", _0)] + MissingElement(String), + #[fail(display = "the content scraped from the Lodestone was invalid: {}", _0)] + InvalidContent(String), + + #[fail(display = "invalid number: {}", _0)] + InvalidNumber(std::num::ParseIntError), + #[fail(display = "invalid url: {}", _0)] + InvalidUrl(url::ParseError), +} + +impl Error { + pub fn missing_element(select: &scraper::Selector) -> Self { + use cssparser::ToCss; + let css = select.selectors.iter().map(|x| x.to_css_string()).collect::>().join(" "); + Error::MissingElement(css) + } + + pub fn invalid_content(expecting: &str, found: Option<&str>) -> Self { + let s = match found { + Some(f) => format!("expecting `{}`, found `{}`", expecting, f), + None => format!("expecting `{}`", expecting), + }; + Error::InvalidContent(s) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ce38930 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ +#![feature(macro_at_most_once_rep)] + +#[macro_use] extern crate failure; +#[macro_use] extern crate lazy_static; +#[macro_use] extern crate serde_derive; + +pub mod error; +pub mod logic; +pub mod models; diff --git a/src/logic.rs b/src/logic.rs new file mode 100644 index 0000000..e6b1485 --- /dev/null +++ b/src/logic.rs @@ -0,0 +1,12 @@ +macro_rules! selectors { + ($($name:ident => $selector:expr);+$(;)?) => { + lazy_static! { + $( + static ref $name: scraper::Selector = scraper::Selector::parse($selector).unwrap(); + )+ + } + } +} + +pub mod character; +pub mod free_company; diff --git a/src/logic/character.rs b/src/logic/character.rs new file mode 100644 index 0000000..eaff1a7 --- /dev/null +++ b/src/logic/character.rs @@ -0,0 +1,106 @@ +use crate::models::character::{ + Character, + CityState, + Clan, + Gender, + GrandCompany, + GrandCompanyInfo, + Guardian, + Race, +}; + +use scraper::Html; + +use std::str::FromStr; + +selectors!( + PROFILE_NAME => ".frame__chara__name"; + PROFILE_WORLD => ".frame__chara__world"; + PROFILE_TITLE => ".frame__chara__title"; + PROFILE_NAME_DAY => ".character-block__birth"; + PROFILE_RACE_CLAN_GENDER => "div.character-block:nth-of-type(1) > .character-block__box > .character-block__name"; + PROFILE_GUARDIAN => "div.character-block:nth-of-type(2) > .character-block__box > .character-block__name"; + PROFILE_CITY_STATE => "div.character-block:nth-of-type(3) > .character-block__box > .character-block__name"; + PROFILE_GRAND_COMPANY => "div.character-block:nth-of-type(4) > .character-block__box > .character-block__name"; + PROFILE_FREE_COMPANY => ".character__freecompany__name > h4 > a"; + PROFILE_TEXT => ".character__selfintroduction"; +); + +pub fn parse(id: u64, html: &str) -> Option { + let html = Html::parse_document(html); + + let name = html.select(&*PROFILE_NAME).next()?.text().collect(); + + let world_str: String = html.select(&*PROFILE_WORLD).next()?.text().collect(); + let world = ffxiv_types::World::from_str(&world_str).ok()?; + + let title: Option = html + .select(&*PROFILE_TITLE) + .next() + .map(|x| x.text().collect()); + + let mut rcg = html.select(&*PROFILE_RACE_CLAN_GENDER).next()?.text(); + + let race = Race::parse(rcg.next()?)?; + + let mut clan_gender_str = rcg.next()?.split(" / "); + + let clan = Clan::parse(clan_gender_str.next()?)?; + + let gender = Gender::parse(clan_gender_str.next()?)?; + + let name_day = html.select(&*PROFILE_NAME_DAY).next()?.text().collect(); + + let guardian_str: String = html.select(&*PROFILE_GUARDIAN).next()?.text().collect(); + let guardian = Guardian::parse(&guardian_str)?; + + let city_state_str: String = html.select(&*PROFILE_CITY_STATE).next()?.text().collect(); + let city_state = CityState::parse(&city_state_str)?; + + let grand_company: Option = html + .select(&*PROFILE_GRAND_COMPANY) + .next() + .map(|x| x.text().collect::()) + .and_then(|x| { + let mut x = x.split(" / "); + let gc = GrandCompany::parse(x.next()?)?; + Some(GrandCompanyInfo { + grand_company: gc, + rank: x.next()?.to_string(), + }) + }); + + let free_company_id: Option = html + .select(&*PROFILE_FREE_COMPANY) + .next() + .and_then(|x| x.value().attr("href")) + .and_then(|x| x + .split('/') + .filter(|x| !x.is_empty()) + .last()) + .and_then(|x| x.parse().ok()); + + let profile_text = html + .select(&*PROFILE_TEXT) + .next()? + .text() + .collect::() + .trim() + .to_string(); + + Some(Character { + id, + name, + world, + race, + clan, + gender, + title, + name_day, + guardian, + city_state, + grand_company, + free_company_id, + profile_text, + }) +} diff --git a/src/logic/free_company.rs b/src/logic/free_company.rs new file mode 100644 index 0000000..76a626d --- /dev/null +++ b/src/logic/free_company.rs @@ -0,0 +1,155 @@ +use crate::{ + error::*, + models::free_company::{FreeCompany, PvpRankings, Estate}, +}; + +use chrono::{DateTime, Local, TimeZone, Utc}; + +use ffxiv_types::World; + +use scraper::Html; + +use url::Url; + +use std::str::FromStr; + +selectors!( + FC_NAME => ".entry__freecompany__name"; + FC_WORLD => "p.entry__freecompany__gc:nth-of-type(3)"; + FC_SLOGAN => ".freecompany__text__message.freecompany__text"; + FC_TAG => ".freecompany__text__tag.freecompany__text"; + FC_CREST => ".entry__freecompany__crest__image > img"; + FC_FORMED => "p.freecompany__text:nth-of-type(5) > script"; + FC_ACTIVE_MEMBERS => "p.freecompany__text:nth-of-type(6)"; + FC_RANK => "p.freecompany__text:nth-of-type(7)"; + FC_WEEKLY_RANKING => ".character__ranking__data tr:nth-of-type(1) > th"; + FC_MONTHLY_RANKING => ".character__ranking__data tr:nth-of-type(2) > th"; + + FC_ESTATE_MISSING => ".freecompany__estate__none"; + FC_ESTATE_NAME => ".freecompany__estate__name"; + FC_ESTATE_ADDRESS => ".freecompany__estate__text"; + FC_ESTATE_GREETING => ".freecompany__estate__greeting"; +); + +pub fn parse(id: u64, html: &str) -> Result { + let html = Html::parse_document(html); + + let name = plain_parse(&html, &*FC_NAME)?; + let world = parse_world(&html)?; + let slogan = plain_parse(&html, &*FC_SLOGAN)?; + let crest = parse_crest(&html)?; + let active_members = parse_active_members(&html)?; + let rank = parse_rank(&html)?; + let pvp_rankings = PvpRankings { + weekly: parse_pvp_rank(&html, &*FC_WEEKLY_RANKING)?, + monthly: parse_pvp_rank(&html, &*FC_MONTHLY_RANKING)?, + }; + let formed = parse_formed(&html)?; + let estate = parse_estate(&html)?; + + Ok(FreeCompany { + name, + world, + slogan, + crest, + active_members, + rank, + pvp_rankings, + formed, + estate, + }) +} + +fn plain_parse(html: &Html, select: &scraper::Selector) -> Result { + let string = html + .select(select) + .next() + .ok_or(Error::missing_element(select))? + .text() + .collect(); + Ok(string) +} + +fn parse_world(html: &Html) -> Result { + let world_str = plain_parse(html, &*FC_WORLD)?; + let trimmed = world_str.trim(); + World::from_str(trimmed) + .map_err(|_| Error::invalid_content("a world", Some(trimmed))) +} + +fn parse_active_members(html: &Html) -> Result { + plain_parse(&html, &*FC_ACTIVE_MEMBERS) + .and_then(|x| x.parse().map_err(Error::InvalidNumber)) +} + +fn parse_rank(html: &Html) -> Result { + plain_parse(&html, &*FC_RANK) + .and_then(|x| x.parse().map_err(Error::InvalidNumber)) +} + +fn parse_pvp_rank(html: &Html, select: &scraper::Selector) -> Result> { + let rank_str = plain_parse(html, select)?; + + let rank = rank_str + .split(":") + .nth(1) + .ok_or_else(|| Error::invalid_content("colon-separated text", Some(&rank_str))) + .and_then(|x| x + .split(" ") + .next() + .ok_or_else(|| Error::invalid_content("space-separated text", Some(&rank_str))))?; + + if rank == "--" { + return Ok(None); + } + + rank + .parse() + .map(Some) + .map_err(Error::InvalidNumber) +} + +fn parse_formed(html: &Html) -> Result> { + let script = html + .select(&*FC_FORMED) + .next() + .ok_or(Error::missing_element(&*FC_FORMED))? + .inner_html(); + + let timestamp = script + .split("strftime(") + .nth(1) + .ok_or(Error::invalid_content("strftime call", Some(&script)))? + .split(",") + .next() + .ok_or(Error::invalid_content("comma-separated strftime call", Some(&script)))?; + let timestamp: i64 = timestamp.parse().map_err(Error::InvalidNumber)?; + + let utc = Local.timestamp(timestamp, 0).with_timezone(&Utc); + + Ok(utc) +} + +fn parse_estate(html: &Html) -> Result> { + if html.select(&*FC_ESTATE_MISSING).next().is_some() { + return Ok(None); + } + + let name = plain_parse(html, &*FC_ESTATE_NAME)?; + let address = plain_parse(html, &*FC_ESTATE_ADDRESS)?; + let greeting = plain_parse(html, &*FC_ESTATE_GREETING)?; + + Ok(Some(Estate { + name, + address, + greeting, + })) +} + +fn parse_crest(html: &Html) -> Result> { + html.select(&*FC_CREST) + .into_iter() + .filter_map(|x| x.value().attr("src")) + .map(|x| Url::parse(x).map_err(Error::InvalidUrl)) + .collect() +} diff --git a/src/models.rs b/src/models.rs new file mode 100644 index 0000000..4bc5f65 --- /dev/null +++ b/src/models.rs @@ -0,0 +1,27 @@ +macro_rules! ffxiv_enum { + ($name:ident { $($variant:ident => $str_repr:expr),+$(,)? }) => { + #[derive(Debug, Serialize)] + pub enum $name { + $($variant,)+ + } + + impl $name { + pub fn parse(s: &str) -> Option { + let res = match s { + $($str_repr => $name::$variant,)+ + _ => return None, + }; + Some(res) + } + + pub fn name(&self) -> &str { + match *self { + $($name::$variant => $str_repr,)+ + } + } + } + } +} + +pub mod character; +pub mod free_company; diff --git a/src/models/character.rs b/src/models/character.rs new file mode 100644 index 0000000..3363bc3 --- /dev/null +++ b/src/models/character.rs @@ -0,0 +1,77 @@ +use ffxiv_types::World; + +#[derive(Debug, Serialize)] +pub struct Character { + pub id: u64, + + pub name: String, + pub world: World, + pub race: Race, + pub clan: Clan, + pub gender: Gender, + pub title: Option, + + pub name_day: String, + pub guardian: Guardian, + pub city_state: CityState, + + pub grand_company: Option, + pub free_company_id: Option, + + pub profile_text: String, +} + +#[derive(Debug, Serialize)] +pub struct GrandCompanyInfo { + pub grand_company: GrandCompany, + pub rank: String, +} + +ffxiv_enum!(Gender { + Male => "♂", + Female => "♀", +}); + +ffxiv_enum!(Race { + AuRa => "Au Ra", + Elezen => "Elezen", + Hyur => "Hyur", + Lalafell => "Lalafell", + Miqote => "Miqo'te", + Roegadyn => "Roegadyn", +}); + +ffxiv_enum!(Clan { + Raen => "Raen", + Xaela => "Xaela", + Duskwight => "Duskwight", + Wildwood => "Wildwood", + Highlander => "Highlander", + Midlander => "Midlander", + Dunesfolk => "Dunesfolk", + Plainsfolk => "Plainsfolk", + SeekerOfTheMoon => "Seeker of the Moon", + SeekerOfTheSun => "Seeker of the Sun", + Hellsguard => "Hellsguard", + SeaWolf => "Sea Wolf", +}); + +ffxiv_enum!(GrandCompany { + Flames => "Immortal Flames", + Maelstrom => "Maelstrom", + TwinAdders => "Order of the Twin Adder", +}); + +ffxiv_enum!(Guardian { + Althyk => "Althyk, the Keeper", + Halone => "Halone, the Fury", + Menphina => "Menphina, the Lover", + Oschon => "Oschon, the Wanderer", + Rhalgr => "Rhalgr, the Destroyer", +}); + +ffxiv_enum!(CityState { + Gridania => "Gridania", + LimsaLominsa => "Limsa Lominsa", + UlDah => "Ul'dah", +}); diff --git a/src/models/free_company.rs b/src/models/free_company.rs new file mode 100644 index 0000000..45012d0 --- /dev/null +++ b/src/models/free_company.rs @@ -0,0 +1,44 @@ +use chrono::{DateTime, Utc}; + +use ffxiv_types::World; + +use url::Url; + +#[derive(Debug, Serialize)] +pub struct FreeCompany { + pub name: String, + pub world: World, + pub slogan: String, + #[serde(serialize_with = "multi_url")] + pub crest: Vec, + pub active_members: u16, + pub rank: u8, + pub pvp_rankings: PvpRankings, + pub formed: DateTime, + pub estate: Option, +} + +#[derive(Debug, Serialize)] +pub struct PvpRankings { + pub weekly: Option, + pub monthly: Option, +} + +#[derive(Debug, Serialize)] +pub struct Estate { + pub name: String, + pub address: String, + pub greeting: String, +} + +fn multi_url(urls: &Vec, serializer: S) -> Result + where S: serde::Serializer, +{ + use serde::ser::SerializeSeq; + + let mut seq = serializer.serialize_seq(Some(urls.len()))?; + for url in urls { + seq.serialize_element(&url_serde::Ser::new(url))?; + } + seq.end() +}