commit 1f6342ed3758979a17b232dfdfa1a797137b46d5 Author: Anna Clemens Date: Sun Oct 3 23:17:09 2021 -0400 chore: initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..70d2f92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/.idea +/config.toml diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b9a0e0a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1850 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "anyhow" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "askama" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d298738b6e47e1034e560e5afe63aa488fea34e25ec11b855a76f0d7b8e73134" +dependencies = [ + "askama_derive", + "askama_escape", + "askama_shared", + "mime", + "mime_guess", +] + +[[package]] +name = "askama_derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2925c4c290382f9d2fa3d1c1b6a63fa1427099721ecca4749b154cc9c25522" +dependencies = [ + "askama_shared", + "proc-macro2", + "syn", +] + +[[package]] +name = "askama_escape" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90c108c1a94380c89d2215d0ac54ce09796823cca0fd91b299cfff3b33e346fb" + +[[package]] +name = "askama_shared" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2582b77e0f3c506ec4838a25fa8a5f97b9bed72bb6d3d272ea1c031d8bd373bc" +dependencies = [ + "askama_escape", + "humansize", + "nom", + "num-traits", + "percent-encoding", + "proc-macro2", + "quote", + "serde", + "syn", + "toml", +] + +[[package]] +name = "askama_warp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b48958817b96cb6898533d3e0c17cfb5d4dba1681a7a236ed4d8f13972e2054" +dependencies = [ + "askama", + "warp", +] + +[[package]] +name = "async-trait" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bson" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dcc15cd0b9aff8e8326561dcf0bb6e56d0e559f3a4897f615b4a5075ab54c46" +dependencies = [ + "ahash", + "base64", + "chrono", + "hex", + "indexmap", + "lazy_static", + "rand", + "serde", + "serde_bytes", + "serde_json", + "uuid", +] + +[[package]] +name = "bumpalo" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "time", + "winapi", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb" +dependencies = [ + "chrono", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "darling" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "enum-as-inner" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ffxiv_types" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f835134aaefd69115c97869d471d410d1852538db6be3d750d8a1168c2f985" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "futures" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" + +[[package]] +name = "futures-executor" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" + +[[package]] +name = "futures-macro" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +dependencies = [ + "autocfg", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" + +[[package]] +name = "futures-task" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" + +[[package]] +name = "futures-util" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +dependencies = [ + "autocfg", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c06815895acec637cd6ed6e9662c935b866d20a106f8361892893a7d9234964" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "headers" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b7591fb62902706ae8e7aaff416b1b0fa2c0fd0878b46dc13baa3712d8a855" +dependencies = [ + "base64", + "bitflags", + "bytes", + "headers-core", + "http", + "mime", + "sha-1", + "time", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" + +[[package]] +name = "humansize" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" + +[[package]] +name = "hyper" +version = "0.14.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d1cfb9e4f68655fa04c01f59edb405b6074a0f7118ea881e5026e4a1cd8593" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipconfig" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" +dependencies = [ + "socket2 0.3.19", + "widestring", + "winapi", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer", + "digest", + "opaque-debug", +] + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "mongodb" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b465d2f8add538efbaa3f22ae63be191b871ca511413405a59b8247cf2f6ef9" +dependencies = [ + "async-trait", + "base64", + "bitflags", + "bson", + "chrono", + "derivative", + "futures-core", + "futures-executor", + "futures-io", + "futures-util", + "hex", + "hmac", + "lazy_static", + "md-5", + "os_info", + "pbkdf2", + "percent-encoding", + "rand", + "rustls", + "serde", + "serde_bytes", + "serde_with", + "sha-1", + "sha2", + "socket2 0.4.2", + "stringprep", + "strsim", + "take_mut", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "trust-dns-proto", + "trust-dns-resolver", + "typed-builder", + "uuid", + "version_check", + "webpki", + "webpki-roots", +] + +[[package]] +name = "nom" +version = "6.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6" +dependencies = [ + "bitvec", + "funty", + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "os_info" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac91020bfed8cc3f8aa450d4c3b5fa1d3373fc091c8a92009f3b27749d5a227" +dependencies = [ + "log", + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remote-party-finder" +version = "0.1.0" +dependencies = [ + "anyhow", + "askama", + "askama_warp", + "base64", + "bitflags", + "chrono", + "chrono-humanize", + "ffxiv_types", + "lazy_static", + "maplit", + "mime", + "mongodb", + "serde", + "serde_json", + "serde_repr", + "sestring", + "tokio", + "tokio-stream", + "toml", + "warp", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustversion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062b87e45d8f26714eacfaef0ed9a583e2bfd50ebd96bdd3c200733bd5758e2c" +dependencies = [ + "rustversion", + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c1fcca18d55d1763e1c16873c4bde0ac3ef75179a28c7b372917e0494625be" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sestring" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e73e6e1c44bc1fcc8b6e2b3d656ac7760819aebe2a4a2e8c0d84e782b45c1de6" +dependencies = [ + "byteorder", + "serde", + "thiserror", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "slab" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if", + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4eac2e6c19f5c3abc0c229bea31ff0b9b091c7b14990e8924b92902a303a0c0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "154794c8f499c2619acd19e839294703e9e32e7630ef5f46ea80d4ef0fbee5eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "trust-dns-proto" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0d7f5db438199a6e2609debe3f69f808d074e0a2888ee0bccb45fe234d03f4" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "lazy_static", + "log", + "rand", + "smallvec", + "thiserror", + "tinyvec", + "tokio", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ad17b608a64bd0735e67bde16b0636f8aa8591f831a25d18443ed00a699770" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lazy_static", + "log", + "lru-cache", + "parking_lot", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "trust-dns-proto", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typed-builder" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a46ee5bd706ff79131be9c94e7edcb82b703c487766a114434e5790361cf08c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "warp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332d47745e9a0c38636dbd454729b147d16bd1ed08ae67b3ab281c4506771054" +dependencies = [ + "bytes", + "futures", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-stream", + "tokio-util", + "tower-service", + "tracing", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki", +] + +[[package]] +name = "widestring" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0887d71 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "remote-party-finder" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +askama = { version = "0.10", features = ["with-warp"] } +askama_warp = "0.11" +base64 = "0.13" +bitflags = "1" +chrono = { version = "0.4", features = ["serde"] } +chrono-humanize = "0.2" +ffxiv_types = "1" +lazy_static = "1" +maplit = "1" +mime = "0.3" +mongodb = { version = "2", features = ["bson-chrono-0_4"] } +sestring = { version = "0.1", features = ["serde"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +serde_repr = "0.1" +tokio = { version = "1", features = ["rt-multi-thread", "macros"] } +tokio-stream = "0.1" +toml = "0.5" +warp = { version = "0.3", default-features = false } + +[dev-dependencies] +lazy_static = "1" diff --git a/assets/icons.svg b/assets/icons.svg new file mode 100644 index 0000000..87fef80 --- /dev/null +++ b/assets/icons.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..fa62937 --- /dev/null +++ b/config.example.toml @@ -0,0 +1,5 @@ +[web] +host = "127.0.0.1:1234" + +[mongo] +url = "mongodb://example.com:1234" diff --git a/src/base64_sestring.rs b/src/base64_sestring.rs new file mode 100644 index 0000000..5b7d916 --- /dev/null +++ b/src/base64_sestring.rs @@ -0,0 +1,17 @@ +use sestring::SeString; +use serde::{Deserializer, Deserialize, Serializer, Serialize}; + +pub fn deserialize<'de, D>(de: D) -> Result + where D: Deserializer<'de>, +{ + let b64 = String::deserialize(de)?; + let bytes = base64::decode(&b64).map_err(|e| serde::de::Error::custom(format!("invalid base64: {:?}", e)))?; + SeString::parse(&bytes).map_err(|e| serde::de::Error::custom(format!("invalid sestring: {:?}", e))) +} + +pub fn serialize(sestring: &SeString, ser: S) -> Result + where S: Serializer, +{ + let bytes = sestring.encode(); + base64::encode(&bytes).serialize(ser) +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..9ab3563 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,18 @@ +use std::net::SocketAddr; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Config { + pub web: Web, + pub mongo: Mongo, +} + +#[derive(Deserialize)] +pub struct Web { + pub host: SocketAddr, +} + +#[derive(Deserialize)] +pub struct Mongo { + pub url: String, +} diff --git a/src/ffxiv.rs b/src/ffxiv.rs new file mode 100644 index 0000000..6eccb7c --- /dev/null +++ b/src/ffxiv.rs @@ -0,0 +1,15 @@ +pub mod auto_translate; +pub mod duties; +pub mod jobs; +pub mod roulettes; +pub mod territory_names; +pub mod worlds; + +pub use self::{ + auto_translate::AUTO_TRANSLATE, + duties::DUTIES, + jobs::JOBS, + roulettes::ROULETTES, + territory_names::TERRITORY_NAMES, + worlds::WORLDS, +}; diff --git a/src/ffxiv/auto_translate.rs b/src/ffxiv/auto_translate.rs new file mode 100644 index 0000000..8e6c916 --- /dev/null +++ b/src/ffxiv/auto_translate.rs @@ -0,0 +1,1266 @@ +use std::collections::HashMap; + +lazy_static::lazy_static! { + pub static ref AUTO_TRANSLATE: HashMap<(u32, u32), &'static str> = maplit::hashmap! { + (1, 100) => "【Languages】", + (1, 101) => "Please use the auto-translate function.", + (1, 102) => "Japanese language", + (1, 103) => "English language", + (1, 104) => "French language", + (1, 105) => "German language", + (1, 106) => "Can you speak Japanese?", + (1, 107) => "Can you speak English?", + (1, 108) => "Can you speak French?", + (1, 109) => "Can you speak German?", + (1, 110) => "I don't speak any English.", + (1, 111) => "I don't speak any Japanese.", + (1, 112) => "I don't speak any French.", + (1, 113) => "I don't speak any German.", + (1, 114) => "Please listen.", + (1, 115) => "Can you hear me?", + (1, 116) => "I can speak a little.", + (1, 117) => "I can understand a little.", + (1, 118) => "Please use simple words.", + (1, 119) => "Please do not abbreviate your words.", + (1, 120) => "I need some time to put together my answer.", + (1, 121) => "What is your native language?", + (2, 200) => "【Greetings】", + (2, 201) => "Nice to meet you.", + (2, 202) => "Good morning!", + (2, 203) => "Hello!", + (2, 204) => "Good evening!", + (2, 205) => "Good night!", + (2, 206) => "Good-bye.", + (2, 207) => "I had fun today!", + (2, 208) => "See you again!", + (2, 209) => "Let's play together again sometime!", + (2, 210) => "I'm back!", + (2, 211) => "Welcome back.", + (2, 212) => "Congratulations!", + (2, 213) => "Good job!", + (2, 214) => "Good luck!", + (2, 215) => "All right!", + (2, 216) => "Thank you.", + (2, 217) => "You're welcome.", + (2, 218) => "Take care.", + (2, 219) => "I'm sorry.", + (2, 220) => "Please forgive me.", + (2, 221) => "That's too bad.", + (2, 222) => "Excuse me...", + (2, 223) => "Have a safe journey.", + (2, 224) => "This is my first time here.", + (2, 225) => "I'm looking forward to it!", + (2, 226) => "Good game!", + (2, 227) => "Let's do it!", + (2, 228) => "Thank you. I must now take my leave.", + (2, 229) => "Let's have some fun!", + (2, 230) => "I'll do my best!", + (2, 231) => "Let's give it our best shot!", + (2, 232) => "This should be fun!", + (2, 233) => "If we stay cool, everything will be fine!", + (3, 300) => "【Questions】", + (3, 301) => "Who?", + (3, 302) => "Which?", + (3, 303) => "How?", + (3, 304) => "What?", + (3, 305) => "When?", + (3, 306) => "How many?", + (3, 307) => "Where?", + (3, 308) => "Why?", + (3, 309) => "How come?", + (3, 310) => "Where shall we go?", + (3, 311) => "Can you do it?", + (3, 312) => "Do you need any help?", + (3, 313) => "Which levequest shall we do?", + (3, 314) => "Which duties can you do?", + (3, 315) => "Do you have it?", + (3, 316) => "What weapons can you use?", + (3, 317) => "What other classes can you use?", + (3, 318) => "What other roles can you cover?", + (3, 319) => "Do you have it set?", + (3, 320) => "What's the battle plan?", + (3, 321) => "Can I add you to my friend list?", + (3, 322) => "Shall we take a break?", + (3, 323) => "Do you want me to repair it?", + (3, 324) => "Can you repair it for me?", + (3, 325) => "Do you want me to meld materia to it?", + (3, 326) => "Can you meld materia for me?", + (3, 327) => "Would you join my party?", + (3, 328) => "Can I join your party?", + (3, 329) => "Should we disband?", + (3, 330) => "Do you want to join my linkshell?", + (3, 331) => "Can I join your linkshell?", + (3, 332) => "Do you want to join my free company?", + (3, 333) => "Can I join your free company?", + (3, 334) => "What are you doing?", + (3, 335) => "How about a game of Triple Triad?", + (3, 336) => "Can we change the rules?", + (3, 337) => "How about joining a Triple Triad tournament?", + (3, 338) => "Do you have enough MP?", + (3, 339) => "Is there anything you don't understand?", + (3, 340) => "Did you see it?", + (3, 341) => "How should we handle this?", + (3, 342) => "What's our strategy?", + (3, 343) => "Do you want to ride together?", + (3, 344) => "Can I ride with you?", + (4, 400) => "【Answers】", + (4, 401) => "I don't understand.", + (4, 402) => "No thanks.", + (4, 403) => "Yes, please.", + (4, 404) => "If you would be so kind.", + (4, 405) => "Understood.", + (4, 406) => "I'm sorry. I'm busy now.", + (4, 407) => "I'm playing solo right now.", + (4, 408) => "I don't know how to answer that question.", + (4, 409) => "I see.", + (4, 410) => "Thanks for the offer, but I'll have to pass.", + (4, 411) => "That's interesting.", + (4, 412) => "Um...", + (4, 413) => "Huh!?", + (4, 414) => "Really?", + (4, 415) => "Hmmm.", + (4, 416) => "I have to go soon.", + (4, 417) => "I'd like another match.", + (4, 418) => "Let's call it a day.", + (4, 419) => "I'd like to take a break.", + (4, 420) => "Probably.", + (5, 500) => "【Reasons】", + (5, 501) => "Casting spell.", + (5, 502) => "Time for work!", + (5, 503) => "I have plans.", + (5, 504) => "I'm sleepy.", + (5, 505) => "I'm tired.", + (5, 506) => "Have stuff to do, gotta go!", + (5, 507) => "I don't feel well.", + (5, 508) => "I'm not up for it.", + (5, 509) => "I'm interested.", + (5, 510) => "I'm bound by duty.", + (5, 511) => "Fighting right now!", + (5, 512) => "I want to make money.", + (5, 513) => "I don't remember.", + (5, 514) => "I don't know.", + (5, 515) => "Just used it.", + (5, 516) => "I want experience points.", + (5, 517) => "Oops!", + (5, 518) => "I already have an invite.", + (5, 519) => "I already have one.", + (5, 520) => "No longer recruiting.", + (5, 521) => "I'm getting ready now.", + (5, 522) => "My hands are full.", + (5, 523) => "We're chewing the fat.", + (5, 524) => "I seem to have misplaced my keyboard.", + (5, 525) => "I'm checking things out.", + (6, 600) => "【Trade】", + (6, 601) => "Can I have it?", + (6, 602) => "Can you do it for me?", + (6, 603) => "Lower the price?", + (6, 604) => "Buy?", + (6, 605) => "Sell?", + (6, 606) => "Trade?", + (6, 607) => "Do you need it?", + (6, 608) => "Can you make it?", + (6, 609) => "Do you have it?", + (6, 610) => "What materials are needed?", + (6, 611) => "I don't have any money.", + (6, 612) => "I don't have the item.", + (6, 613) => "I don't have anything to give you.", + (6, 614) => "You can have this.", + (6, 615) => "Please.", + (6, 616) => "Reward:", + (6, 617) => "Price:", + (6, 618) => "delivery", + (7, 700) => "【Organize】", + (7, 701) => "Looking for members.", + (7, 702) => "Gather together.", + (7, 703) => "Team up?", + (7, 704) => "Are you alone?", + (7, 705) => "Any vacancies?", + (7, 706) => "Please invite me.", + (7, 707) => "Please let me join.", + (7, 708) => "Who is the leader?", + (7, 709) => "Just for a short time is fine.", + (7, 710) => "Our party's full.", + (7, 711) => "Please assist.", + (7, 712) => "Disbanding party.", + (7, 713) => "Taking a break.", + (7, 714) => "Looking for party.", + (7, 715) => "light party", + (7, 716) => "full party", + (7, 717) => "I'm inexperienced.", + (7, 718) => "Should we find some replacements?", + (8, 800) => "【Tactics】", + (8, 801) => "Please follow.", + (8, 802) => "I'll follow you.", + (8, 803) => "Please check it.", + (8, 804) => "Found it!", + (8, 805) => "Full attack!", + (8, 806) => "Pull back.", + (8, 807) => "Watch your aggro!", + (8, 808) => "Defeat this one first!", + (8, 809) => "Please don't attack.", + (8, 810) => "Hold the target!", + (8, 811) => "Please deactivate it.", + (8, 812) => "Heal!", + (8, 813) => "Cast it!", + (8, 814) => "Run away!", + (8, 815) => "Help me out!", + (8, 816) => "Stop!", + (8, 817) => "Standing by.", + (8, 818) => "None left.", + (8, 819) => "Don't have it.", + (8, 820) => "Please use it sparingly.", + (8, 821) => "I'll use it sparingly.", + (8, 822) => "My gear is in poor condition.", + (8, 823) => "Ready!", + (8, 824) => "Please set enemy marks.", + (8, 825) => "Please use it.", + (8, 826) => "Let's rest for a while.", + (8, 827) => "Pulling enemy over.", + (8, 828) => "Drawing enemy over.", + (8, 829) => "Wait, please.", + (8, 830) => "Let's meet there.", + (8, 831) => "Let's move.", + (8, 832) => "Stay there.", + (8, 833) => "On my way.", + (8, 834) => "Will be right back.", + (8, 835) => "Roll for loot, please.", + (8, 836) => "Weakness:", + (8, 837) => "Beware of", + (8, 838) => "Recommended:", + (8, 839) => "Kill Order:", + (8, 840) => "I want", + (8, 841) => "Please draw enmity.", + (8, 842) => "The enemy is near!", + (8, 843) => "Don't worry about it.", + (8, 844) => "Please ignore that.", + (8, 845) => "Switch.", + (8, 846) => "Change with me.", + (8, 847) => "I'll deal the first blow.", + (8, 848) => "Let's take it slow.", + (8, 849) => "Do it!", + (8, 850) => "Direction:", + (8, 851) => "Avoid the attack!", + (8, 852) => "Turn away.", + (8, 853) => "Get hit.", + (8, 854) => "Pull the enemy away.", + (8, 855) => "Move away.", + (8, 856) => "Come closer.", + (8, 857) => "Carry it.", + (8, 859) => "Cut the link!", + (8, 860) => "Keep it busy.", + (8, 861) => "Take them down at the same time!", + (8, 862) => "Hide!", + (8, 863) => "Mounting machina!", + (8, 864) => "Mount the machina!", + (9, 900) => "【Roles】", + (9, 901) => "leader", + (9, 902) => "master", + (9, 903) => "Disciples of War", + (9, 904) => "Disciples of Magic", + (9, 905) => "Disciples of the Land", + (9, 906) => "Disciples of the Hand", + (9, 907) => "tank", + (9, 908) => "melee", + (9, 909) => "ranged", + (9, 910) => "healer", + (9, 911) => "buffer", + (9, 912) => "caster", + (9, 913) => "DPS", + (10, 950) => "【Locations】", + (10, 951) => "position", + (10, 952) => "north", + (10, 953) => "south", + (10, 954) => "east", + (10, 955) => "west", + (10, 956) => "up", + (10, 957) => "down", + (10, 958) => "right", + (10, 959) => "left", + (10, 960) => "front side", + (10, 961) => "back", + (10, 962) => "side", + (10, 963) => "front", + (10, 964) => "middle", + (10, 965) => "flank", + (10, 966) => "inside", + (10, 967) => "outside", + (10, 968) => "this way", + (10, 969) => "over there", + (10, 970) => "that way", + (10, 971) => "closer", + (10, 972) => "farther", + (10, 973) => "entrance", + (10, 974) => "exit", + (10, 975) => "rear", + (10, 976) => "one o'clock", + (10, 977) => "two o'clock", + (10, 978) => "three o'clock", + (10, 979) => "four o'clock", + (10, 980) => "five o'clock", + (10, 981) => "six o'clock", + (10, 982) => "seven o'clock", + (10, 983) => "eight o'clock", + (10, 984) => "nine o'clock", + (10, 985) => "ten o'clock", + (10, 986) => "eleven o'clock", + (10, 987) => "twelve o'clock", + (11, 1000) => "【Time】", + (11, 1001) => "day before yesterday", + (11, 1002) => "yesterday", + (11, 1003) => "today", + (11, 1004) => "tomorrow", + (11, 1005) => "day after tomorrow", + (11, 1006) => "last week", + (11, 1007) => "this week", + (11, 1008) => "next week", + (11, 1009) => "a.m.", + (11, 1010) => "p.m.", + (11, 1011) => "morning", + (11, 1012) => "afternoon", + (11, 1013) => "night", + (11, 1014) => "day of the week", + (11, 1015) => "Sunday", + (11, 1016) => "Monday", + (11, 1017) => "Tuesday", + (11, 1018) => "Wednesday", + (11, 1019) => "Thursday", + (11, 1020) => "Friday", + (11, 1021) => "Saturday", + (11, 1022) => "holiday", + (11, 1023) => "break", + (11, 1024) => "long time", + (11, 1025) => "short time", + (11, 1026) => "date", + (11, 1027) => "second", + (11, 1028) => "minute", + (11, 1029) => "hour", + (11, 1030) => "month", + (11, 1031) => "year", + (11, 1032) => "January", + (11, 1033) => "February", + (11, 1034) => "March", + (11, 1035) => "April", + (11, 1036) => "May", + (11, 1037) => "June", + (11, 1038) => "July", + (11, 1039) => "August", + (11, 1040) => "September", + (11, 1041) => "October", + (11, 1042) => "November", + (11, 1043) => "December", + (11, 1044) => "last month", + (11, 1045) => "this month", + (11, 1046) => "next month", + (11, 1047) => "last year", + (11, 1048) => "this year", + (11, 1049) => "next year", + (11, 1050) => "now", + (11, 1051) => "soon", + (11, 1052) => "any time", + (11, 1053) => "first", + (11, 1054) => "last", + (11, 1055) => "waiting time", + (11, 1056) => "time remaining", + (11, 1057) => "end time", + (11, 1058) => "Sixth Astral Era", + (11, 1059) => "Seventh Umbral Era", + (11, 1060) => "Seventh Astral Era", + (11, 1061) => "before", + (11, 1062) => "after", + (13, 1150) => "【Transportation】", + (13, 1151) => "walk", + (13, 1152) => "run", + (13, 1153) => "follow", + (13, 1154) => "chocobo", + (13, 1155) => "personal chocobo", + (13, 1156) => "rental chocobo", + (13, 1157) => "chocobo porter", + (13, 1158) => "mount", + (13, 1159) => "Teleport", + (13, 1160) => "Return", + (13, 1161) => "Aethernet", + (13, 1162) => "airship", + (13, 1163) => "Home Point", + (13, 1164) => "ferry", + (13, 1165) => "flying mount", + (14, 1200) => "【Facilities】", + (14, 1201) => "aetheryte", + (14, 1202) => "city-state aetheryte", + (14, 1203) => "Adventurers' Guild", + (14, 1204) => "guild", + (14, 1205) => "shop", + (14, 1206) => "chocobo stop", + (14, 1207) => "inn", + (14, 1208) => "summoning bell", + (14, 1209) => "sanctuary", + (14, 1210) => "levemete", + (14, 1211) => "linkshell distributor", + (14, 1212) => "market board", + (14, 1213) => "retainer vocate", + (14, 1214) => "armor mender", + (14, 1215) => "landing", + (14, 1216) => "delivery moogle", + (14, 1217) => "docks", + (14, 1218) => "ferry docks", + (14, 1219) => "landing", + (14, 1220) => "Maelstrom Command", + (14, 1221) => "The Adders' Nest", + (14, 1222) => "The Hall of Flames", + (14, 1223) => "residential district", + (14, 1224) => "entry counter", + (14, 1225) => "Hunt board", + (14, 1226) => "skywatcher", + (14, 1227) => "crystal bell", + (14, 1228) => "subdivision", + (14, 1229) => "orchestrion", + (14, 1230) => "striking dummy", + (14, 1231) => "materia melder", + (14, 1232) => "apartment", + (14, 1233) => "glamour dresser", + (14, 1234) => "message book", + (14, 1235) => "armoire", + (14, 1236) => "The Unending Journey", + (15, 1250) => "【Grand Company】", + (15, 1251) => "Grand Company", + (15, 1252) => "Grand Company rank", + (15, 1253) => "supplies", + (15, 1254) => "provisions", + (15, 1255) => "high-quality supplies", + (15, 1256) => "Maelstrom", + (15, 1257) => "Order of the Twin Adder", + (15, 1258) => "Immortal Flames", + (16, 1270) => "【Free Company】", + (16, 1271) => "free company", + (16, 1272) => "signature", + (16, 1273) => "company board", + (16, 1274) => "company chest", + (16, 1275) => "company crest", + (16, 1276) => "company action", + (16, 1277) => "company crafting", + (16, 1278) => "company workshop", + (16, 1279) => "exploratory voyage", + (16, 1280) => "subaquatic voyage", + (17, 1300) => "【System】", + (17, 1301) => "class", + (17, 1302) => "class change", + (17, 1303) => "job", + (17, 1304) => "job change", + (17, 1305) => "soul crystal", + (17, 1306) => "level", + (17, 1307) => "experience points", + (17, 1308) => "guardian", + (17, 1309) => "race", + (17, 1310) => "clan", + (17, 1311) => "area", + (17, 1312) => "Armoury", + (17, 1313) => "item", + (17, 1314) => "gear set", + (17, 1315) => "rested bonus", + (17, 1316) => "solo", + (17, 1317) => "party", + (17, 1318) => "mount", + (17, 1319) => "minion", + (17, 1320) => "retainer", + (17, 1321) => "battle", + (17, 1322) => "crafting", + (17, 1323) => "gathering", + (17, 1324) => "durability", + (17, 1325) => "spiritbond", + (17, 1326) => "gear affinity", + (17, 1327) => "treasure coffer", + (17, 1328) => "target", + (17, 1329) => "lock on", + (17, 1330) => "focus target", + (17, 1331) => "enemy sign", + (17, 1332) => "menu", + (17, 1333) => "map", + (17, 1334) => "support desk", + (17, 1335) => "screenshot", + (17, 1336) => "log out", + (17, 1337) => "exit game", + (17, 1338) => "shutdown", + (17, 1339) => "auto-translation dictionary", + (17, 1340) => "emote", + (17, 1341) => "achievements", + (17, 1342) => "title", + (17, 1343) => "currency", + (17, 1344) => "gil", + (17, 1345) => "trade", + (17, 1346) => "examine", + (17, 1347) => "auto-follow", + (17, 1348) => "alliance", + (17, 1349) => "housing", + (17, 1350) => "reputation", + (17, 1351) => "reputation rank", + (17, 1352) => "commendation", + (17, 1353) => "Allagan tomestone", + (17, 1356) => "vote dismiss", + (17, 1357) => "vote abandon", + (17, 1358) => "waymark", + (17, 1359) => "ground target", + (17, 1360) => "glamours", + (17, 1362) => "aesthetician", + (17, 1363) => "retainer ventures", + (17, 1364) => "atma", + (17, 1365) => "Zodiac Weapon", + (17, 1366) => "alexandrite", + (17, 1367) => "animus", + (17, 1368) => "novus", + (17, 1369) => "Allied Seals", + (17, 1370) => "mark bill", + (17, 1371) => "Frontline", + (17, 1372) => "The Echo", + (17, 1373) => "chocobo raising", + (17, 1374) => "weather report", + (17, 1375) => "Allagan tomestone of poetics", + (17, 1376) => "quota points", + (17, 1377) => "company seals", + (17, 1378) => "Wolf Marks", + (17, 1379) => "Zenith", + (17, 1380) => "Nexus", + (17, 1381) => "Centurio Seal", + (17, 1384) => "Loot Rule: Lootmaster", + (17, 1385) => "Loot Rule: Greed Only", + (17, 1386) => "Loot Rule: Normal", + (17, 1387) => "Anima Weapon", + (17, 1389) => "Ceremony of Eternal Bonding", + (17, 1390) => "item level", + (17, 1391) => "quest sync", + (17, 1392) => "Wondrous Tails", + (17, 1396) => "undersized party", + (17, 1397) => "performance", + (17, 1398) => "Fashion Report", + (18, 1400) => "【Battle】", + (18, 1401) => "attack", + (18, 1402) => "action", + (18, 1403) => "general action", + (18, 1404) => "trait", + (18, 1405) => "job action", + (18, 1406) => "additional action", + (18, 1407) => "magic", + (18, 1408) => "weaponskill", + (18, 1409) => "ability", + (18, 1410) => "cast time", + (18, 1411) => "recast time", + (18, 1412) => "affinity", + (18, 1413) => "link", + (18, 1414) => "pet", + (18, 1415) => "companion", + (18, 1416) => "enmity", + (18, 1417) => "active", + (18, 1418) => "passive", + (18, 1419) => "KO'd", + (18, 1420) => "loot", + (18, 1421) => "divvy loot", + (18, 1422) => "lot", + (18, 1423) => "resist", + (18, 1424) => "acquire", + (18, 1425) => "aggro", + (18, 1426) => "area of effect", + (18, 1427) => "limit break", + (18, 1428) => "enemy", + (18, 1429) => "monster", + (18, 1430) => "notorious monster", + (18, 1432) => "EXP chain", + (18, 1433) => "Armoury bonus", + (18, 1434) => "level sync", + (18, 1435) => "PvP action", + (18, 1436) => "area of effect attack", + (18, 1437) => "frontal area of effect attack", + (18, 1438) => "rear area of effect attack", + (18, 1439) => "whole area of effect attack", + (18, 1440) => "outpost", + (18, 1441) => "Allagan Manors", + (18, 1442) => "stronghold", + (18, 1443) => "standard", + (18, 1444) => "occupied", + (18, 1445) => "unoccupied", + (18, 1446) => "Tactical Rating", + (18, 1447) => "Allagan Markets", + (18, 1448) => "Allagan Sun Temple", + (18, 1449) => "combo", + (18, 1450) => "adrenaline rush", + (18, 1451) => "single target attack", + (18, 1452) => "ballista", + (18, 1453) => "Allagan tomelith", + (18, 1454) => "The Claws", + (18, 1455) => "The Fangs", + (18, 1456) => "medal", + (18, 1458) => "Culling Time", + (18, 1462) => "adrenaline", + (18, 1466) => "adrenaline kit", + (18, 1467) => "supply box", + (18, 1468) => "K - Knockout", + (18, 1469) => "D - Down", + (18, 1470) => "A - Assist", + (18, 1471) => "chain", + (18, 1472) => "charge", + (18, 1473) => "knockback", + (18, 1474) => "draw in", + (18, 1475) => "icebound tomelith", + (18, 1476) => "data retrieved", + (18, 1477) => "wolf's heart", + (18, 1478) => "wolf's heart kit", + (18, 1479) => "core", + (18, 1480) => "magitek field", + (18, 1481) => "tower", + (18, 1482) => "hangar", + (18, 1483) => "ceruleum", + (18, 1484) => "power generator", + (18, 1485) => "steam cannon", + (18, 1486) => "wind-up viking", + (18, 1487) => "wind-up magus", + (18, 1488) => "machina", + (18, 1489) => "Cruise Chaser", + (18, 1490) => "Oppressor", + (18, 1491) => "Brute Justice", + (18, 1492) => "role action", + (18, 1494) => "quick chat", + (18, 1496) => "ceruleum engine", + (18, 1497) => "gobtank", + (18, 1498) => "goblin mercenary", + (18, 1499) => "tactical rating", + (19, 1500) => "【Online Status】", + (19, 1501) => "Online", + (19, 1502) => "Away", + (19, 1503) => "Busy", + (19, 1504) => "Looking for Party", + (19, 1505) => "Looking to Meld Materia", + (19, 1506) => "Bound by Duty", + (19, 1507) => "Offline", + (19, 1508) => "Recruiting Party Members", + (19, 1509) => "New Adventurer", + (19, 1510) => "Mentor", + (19, 1511) => "PvE Mentor", + (19, 1512) => "PvP Mentor", + (19, 1513) => "Trade Mentor", + (19, 1514) => "Role-playing", + (19, 1515) => "Returner", + (19, 1516) => "Participating in Event", + (18, 1521) => "ovoo", + (18, 1522) => "claim", + (20, 1550) => "【Communication】", + (20, 1551) => "chat", + (20, 1552) => "Tell", + (20, 1553) => "Say", + (20, 1554) => "Shout", + (20, 1555) => "Yell", + (20, 1556) => "friend list", + (20, 1557) => "blacklist", + (20, 1558) => "Player Search", + (20, 1559) => "linkshell", + (20, 1560) => "moogle letter", + (20, 1561) => "Lodestone", + (20, 1562) => "blog", + (20, 1563) => "follow", + (20, 1564) => "forums", + (20, 1565) => "ranking", + (20, 1566) => "Grand Company ranking", + (20, 1567) => "free company ranking", + (20, 1568) => "party", + (20, 1569) => "alliance", + (20, 1570) => "linkshell", + (20, 1571) => "free company", + (20, 1572) => "Novice Network", + (20, 1573) => "PvP Team", + (20, 1574) => "cross-world linkshell", + (20, 1575) => "cross-world linkshell", + (20, 1576) => "contacts", + (20, 1577) => "Community Finder", + (22, 1600) => "【Crafting & Gathering】", + (22, 1601) => "shard", + (22, 1602) => "crystal", + (22, 1603) => "cluster", + (22, 1604) => "materia", + (22, 1605) => "materia extraction", + (22, 1606) => "materia craft", + (22, 1607) => "markets", + (22, 1608) => "market search", + (22, 1609) => "HQ", + (22, 1610) => "Unique", + (22, 1611) => "Untradable", + (22, 1612) => "material", + (22, 1613) => "quick synthesis", + (22, 1614) => "quality", + (22, 1615) => "high-quality", + (22, 1616) => "highest-quality", + (22, 1617) => "repair", + (22, 1618) => "repair materials", + (22, 1619) => "gear dyeing", + (22, 1620) => "mature tree", + (22, 1621) => "logging", + (22, 1622) => "harvesting", + (22, 1623) => "mineral deposit", + (22, 1624) => "mining", + (22, 1625) => "quarrying", + (22, 1626) => "fishing", + (22, 1627) => "materia melding", + (22, 1628) => "advanced materia melding", + (22, 1629) => "gardening", + (22, 1630) => "raising", + (22, 1631) => "harvesting", + (22, 1632) => "master recipe book", + (22, 1633) => "desynthesis", + (22, 1634) => "big fishing", + (22, 1635) => "release", + (22, 1636) => "unspoiled locations", + (22, 1637) => "collectable", + (22, 1638) => "specialist", + (22, 1639) => "DoL favors", + (22, 1640) => "tome of regional folklore", + (22, 1641) => "crafters' scrip", + (22, 1642) => "gatherers' scrip", + (22, 1643) => "red crafters' scrip", + (22, 1644) => "blue crafters' scrip", + (22, 1645) => "red gatherers' scrip", + (22, 1646) => "blue gatherers' scrip", + (22, 1647) => "yellow crafters' scrip", + (22, 1648) => "yellow gatherers' scrip", + (22, 1649) => "white crafters' scrip", + (22, 1650) => "white gatherers' scrip", + (22, 1651) => "quick gathering", + (22, 1652) => "trial synthesis", + (22, 1653) => "skybuilders' scrip", + (22, 1654) => "collectability", + (23, 1900) => "【Primals】", + (23, 1901) => "primal", + (23, 1902) => "Ifrit", + (23, 1903) => "Titan", + (23, 1904) => "Garuda", + (23, 1905) => "Bahamut", + (23, 1906) => "Moggle Mog XII", + (23, 1907) => "beastmen", + (23, 1908) => "Amalj'aa", + (23, 1909) => "sylphs", + (23, 1910) => "Ixal", + (23, 1911) => "kobolds", + (23, 1912) => "Sahagin", + (23, 1913) => "Leviathan", + (23, 1914) => "Ramuh", + (23, 1915) => "Shiva", + (23, 1916) => "Alexander (primal)", + (23, 1917) => "Ravana", + (23, 1918) => "Bismarck", + (23, 1919) => "Gnath", + (23, 1920) => "Vanu Vanu", + (23, 1921) => "goblins", + (23, 1922) => "dragons", + (23, 1923) => "Nidhogg", + (23, 1924) => "Sephirot", + (23, 1925) => "Sophia", + (23, 1926) => "Zurvan", + (23, 1927) => "Kojin", + (23, 1928) => "Ananta", + (23, 1929) => "Susano", + (23, 1930) => "Lakshmi", + (23, 1931) => "Shinryu", + (23, 1932) => "Omega", + (23, 1933) => "Byakko", + (23, 1934) => "Tsukuyomi", + (23, 1935) => "moogle", + (23, 1936) => "Namazu", + (23, 1937) => "Phoenix", + (23, 1938) => "Odin", + (23, 1939) => "Suzaku", + (23, 1940) => "Seiryu", + (23, 1941) => "Titania", + (23, 1942) => "Innocence", + (23, 1943) => "Hades", + (23, 1944) => "Dwarf", + (23, 1945) => "Pixie", + (23, 1946) => "Nu Mou", + (23, 1947) => "Qitari", + (23, 1948) => "Ruby Weapon", + (23, 1949) => "Warrior of Light", + (23, 1950) => "Emerald Weapon", + (23, 1951) => "Diamond Weapon", + (24, 2000) => "【Duty】", + (24, 2001) => "duty", + (24, 2002) => "quest", + (24, 2003) => "main scenario quest", + (24, 2004) => "sidequest", + (24, 2005) => "class quest", + (24, 2006) => "job quest", + (24, 2007) => "Grand Company quest", + (24, 2008) => "FATE", + (24, 2009) => "guildleve", + (24, 2010) => "levequest", + (24, 2011) => "battlecraft leve", + (24, 2012) => "fieldcraft leve", + (24, 2013) => "tradecraft leve", + (24, 2015) => "Grand Company leve", + (24, 2017) => "leve link", + (24, 2018) => "leve allowance", + (24, 2019) => "levequest difficulty", + (24, 2020) => "guildhest", + (24, 2021) => "dungeon", + (24, 2022) => "primal battle", + (24, 2023) => "hard primal battle", + (24, 2024) => "legatus battle", + (24, 2025) => "raid", + (24, 2026) => "The Coliseum", + (24, 2027) => "The Thousand Maws of Toto-Rak", + (24, 2028) => "The Tam-Tara Deepcroft", + (24, 2029) => "Copperbell Mines", + (24, 2030) => "Sastasha", + (24, 2031) => "The Aurum Vale", + (24, 2032) => "Haukke Manor", + (24, 2033) => "Halatali", + (24, 2034) => "Brayflox's Longstop", + (24, 2035) => "The Sunken Temple of Qarn", + (24, 2036) => "The Wanderer's Palace", + (24, 2037) => "The Stone Vigil", + (24, 2038) => "Cutter's Cry", + (24, 2039) => "Dzemael Darkhold", + (24, 2040) => "Amdapor Keep", + (24, 2041) => "Castrum Meridianum", + (24, 2042) => "The Praetorium", + (24, 2043) => "Basic Training: Enemy Parties", + (24, 2044) => "Under the Armor", + (24, 2045) => "Basic Training: Enemy Strongholds", + (24, 2046) => "Hero on the Half Shell", + (24, 2047) => "Pulling Poison Posies", + (24, 2048) => "Stinging Back", + (24, 2049) => "All's Well that Ends in the Well", + (24, 2050) => "Flicking Sticks and Taking Names", + (24, 2051) => "More than a Feeler", + (24, 2052) => "Annoy the Void", + (24, 2053) => "Shadow and Claw", + (24, 2054) => "Long Live the Queen", + (24, 2055) => "Ward Up", + (24, 2056) => "Solemn Trinity", + (24, 2057) => "The Bowl of Embers (Normal)", + (24, 2058) => "The Navel (Normal)", + (24, 2059) => "The Howling Eye (Normal)", + (24, 2060) => "The Bowl of Embers (Hard)", + (24, 2061) => "The Navel (Hard)", + (24, 2062) => "The Howling Eye (Hard)", + (24, 2063) => "Cape Westwind", + (24, 2064) => "The Binding Coil of Bahamut", + (24, 2065) => "The Wolves' Den", + (24, 2066) => "The Bowl of Embers (Extreme)", + (24, 2067) => "The Navel (Extreme)", + (24, 2068) => "The Howling Eye (Extreme)", + (24, 2069) => "Thornmarch (Hard)", + (24, 2070) => "The Minstrel's Ballad: Ultima's Bane", + (24, 2071) => "Crystal Tower", + (24, 2072) => "The Binding Coil of Bahamut", + (24, 2073) => "Labyrinth of the Ancients", + (24, 2074) => "Decipher", + (24, 2075) => "hard mode", + (24, 2076) => "Duty Roulette", + (24, 2077) => "The Second Coil of Bahamut", + (24, 2078) => "Pharos Sirius", + (24, 2079) => "Thornmarch (Extreme)", + (24, 2080) => "The Whorleater (Hard)", + (24, 2081) => "The Whorleater (Extreme)", + (24, 2082) => "A Relic Reborn: The Chimera", + (24, 2083) => "A Relic Reborn: The Hydra", + (24, 2084) => "Battle on the Big Bridge", + (24, 2085) => "PvP", + (24, 2086) => "beast tribe quest", + (24, 2087) => "beast tribe sidequest", + (24, 2088) => "beast tribe main quest", + (24, 2089) => "side story quest", + (24, 2090) => "treasure hunt", + (24, 2091) => "chronicles of a new era quest", + (24, 2092) => "seasonal event", + (24, 2093) => "The Lost City of Amdapor", + (24, 2094) => "Trials of the Braves", + (24, 2095) => "sphere scroll", + (24, 2096) => "The Hunt", + (24, 2097) => "elite marks", + (24, 2098) => "The Striking Tree (Hard)", + (24, 2099) => "The Striking Tree (Extreme)", + (24, 2100) => "Syrcus Tower", + (24, 2101) => "The Second Coil of Bahamut (Savage)", + (24, 2102) => "The Carteneau Flats", + (24, 2103) => "Hullbreaker Isle", + (24, 2104) => "Frontline", + (24, 2106) => "expert", + (24, 2108) => "The Final Coil of Bahamut", + (24, 2109) => "The Akh Afah Amphitheatre (Hard)", + (24, 2110) => "The Akh Afah Amphitheatre (Extreme)", + (24, 2111) => "Snowcloak", + (24, 2112) => "The Dragon's Neck", + (24, 2113) => "delivery quest", + (24, 2114) => "Urth's Fount (Hard)", + (24, 2115) => "The World of Darkness", + (24, 2117) => "The Borderland Ruins (Secure)", + (24, 2119) => "The Keeper of the Lake", + (24, 2120) => "Battle in the Big Keep", + (24, 2121) => "Ascian", + (24, 2122) => "the void", + (24, 2123) => "The Great Gubal Library", + (24, 2124) => "Neverreap", + (24, 2125) => "The Vault", + (24, 2126) => "The Fractal Continuum", + (24, 2127) => "Dusk Vigil", + (24, 2128) => "Sohm Al", + (24, 2129) => "Aetherochemical Research Facility", + (24, 2130) => "The Aery", + (24, 2131) => "Thok ast Thok (Hard)", + (24, 2132) => "Thok ast Thok (Extreme)", + (24, 2133) => "Limitless Blue (Hard)", + (24, 2134) => "Limitless Blue (Extreme)", + (24, 2135) => "Seal Rock (Seize)", + (24, 2137) => "Alexander", + (24, 2138) => "Alexander (Savage)", + (24, 2139) => "Gordias", + (24, 2140) => "The Chrysalis (Normal)", + (24, 2141) => "The Steps of Faith (Normal)", + (24, 2142) => "The Singularity Reactor (Normal)", + (24, 2143) => "Temple leve", + (24, 2144) => "Saint Mocianne's Arboretum", + (24, 2145) => "The Shadow of Mhach", + (24, 2146) => "The Void Ark", + (24, 2147) => "Hall of the Novice", + (24, 2148) => "The Antitower", + (24, 2149) => "Midas", + (24, 2150) => "Containment Bay S1T7 (Normal)", + (24, 2151) => "Containment Bay S1T7 (Extreme)", + (24, 2153) => "The Feast", + (24, 2154) => "Stone, Sky, Sea", + (24, 2155) => "Sohr Khai", + (24, 2156) => "The Weeping City of Mhach", + (24, 2157) => "Deep Dungeon", + (24, 2158) => "Palace of the Dead", + (24, 2159) => "The Final Steps of Faith", + (24, 2160) => "The Minstrel's Ballad: Nidhogg's Rage", + (24, 2161) => "The Fields of Glory (Shatter)", + (24, 2163) => "duel", + (24, 2164) => "Xelphatol", + (24, 2165) => "Containment Bay P1T6", + (24, 2166) => "Containment Bay P1T6 (Extreme)", + (24, 2167) => "The Creator", + (24, 2168) => "Baelsar's Wall", + (24, 2169) => "Dun Scaith", + (24, 2170) => "Containment Bay Z1T9", + (24, 2171) => "Containment Bay Z1T9 (Extreme)", + (24, 2172) => "Shisui of the Violet Tides", + (24, 2173) => "Bardam's Mettle", + (24, 2174) => "Doma Castle", + (24, 2175) => "Castrum Abania", + (24, 2176) => "The Pool of Tribute", + (24, 2177) => "The Pool of Tribute (Extreme)", + (24, 2178) => "Emanation", + (24, 2179) => "Emanation (Extreme)", + (24, 2180) => "The Royal Menagerie", + (24, 2181) => "Interdimensional Rift", + (24, 2182) => "Interdimensional Rift (Savage)", + (24, 2183) => "Deltascape", + (24, 2184) => "The Temple of the Fist", + (24, 2185) => "The Drowned City of Skalla", + (24, 2186) => "The Minstrel's Ballad: Shinryu's Domain", + (24, 2187) => "alliance raid", + (24, 2188) => "Return to Ivalice", + (24, 2189) => "The Royal City of Rabanastre", + (24, 2190) => "The Unending Coil of Bahamut (Ultimate)", + (24, 2191) => "Rival Wings", + (24, 2192) => "Astragalos", + (24, 2193) => "Hells' Lid", + (24, 2194) => "The Jade Stoa", + (24, 2195) => "The Jade Stoa (Extreme)", + (24, 2196) => "Sigmascape", + (24, 2197) => "The Forbidden Land, Eureka", + (24, 2198) => "Anemos", + (28, 2900) => "【Job Gauge】", + (28, 2901) => "Oath Gauge", + (28, 2902) => "Oath", + (28, 2903) => "Lightning Gauge", + (28, 2904) => "Chakra Gauge", + (28, 2905) => "Beast Gauge", + (28, 2906) => "Wrath", + (28, 2907) => "Dragon Gauge", + (28, 2908) => "Gaze of the First Brood", + (28, 2909) => "Song Gauge", + (28, 2910) => "Repertoire", + (28, 2911) => "Healing Gauge", + (28, 2912) => "Lily", + (28, 2913) => "Elemental Gauge", + (28, 2914) => "Umbral Heart", + (28, 2915) => "Aetherflow Gauge", + (28, 2916) => "Aethertrail Attunement", + (28, 2917) => "Trance Gauge", + (28, 2918) => "Dreadwyrm Aether", + (28, 2919) => "Faerie Gauge", + (28, 2920) => "Fae Aether", + (28, 2921) => "Huton Gauge", + (28, 2922) => "Ninki Gauge", + (28, 2923) => "Ninki", + (28, 2926) => "Heat Gauge", + (28, 2927) => "Overheat", + (28, 2928) => "Blood Gauge", + (28, 2930) => "Arcana Gauge", + (28, 2931) => "Sen Gauge", + (28, 2932) => "Sen", + (28, 2933) => "Kenki Gauge", + (28, 2934) => "Kenki", + (28, 2935) => "Balance Gauge", + (28, 2936) => "White Mana", + (28, 2937) => "Black Mana", + (28, 2938) => "Darkside Gauge", + (28, 2939) => "Blood Lily", + (28, 2940) => "Heat Gauge", + (28, 2941) => "Battery Gauge", + (28, 2942) => "Powder Gauge", + (28, 2943) => "Step Gauge", + (28, 2944) => "Fourfold Feathers", + (28, 2945) => "Divination Gauge", + (28, 2946) => "Meditation Gauge", + (25, 3000) => "【Gold Saucer】", + (25, 3001) => "chocobo race", + (25, 3002) => "race chocobo", + (25, 3003) => "fledgling chocobo", + (25, 3004) => "retired chocobo", + (25, 3005) => "covering", + (25, 3006) => "rating", + (25, 3007) => "pedigree level", + (25, 3008) => "course roulette", + (25, 3009) => "Sagolii Road", + (25, 3010) => "Tranquil Paths", + (25, 3011) => "Triple Triad", + (25, 3012) => "card", + (25, 3013) => "card type", + (25, 3014) => "rarity", + (25, 3015) => "match", + (25, 3016) => "tournament", + (25, 3017) => "tourney", + (25, 3018) => "points", + (25, 3019) => "rule variations", + (25, 3020) => "regional rules", + (25, 3021) => "match rules", + (25, 3022) => "tournament rules", + (25, 3023) => "All Open", + (25, 3024) => "Three Open", + (25, 3025) => "Same", + (25, 3026) => "Sudden Death", + (25, 3027) => "Plus", + (25, 3028) => "Random", + (25, 3029) => "Order", + (25, 3030) => "Chaos", + (25, 3031) => "Reverse", + (25, 3032) => "Fallen Ace", + (25, 3033) => "Ascension", + (25, 3034) => "Descension", + (25, 3035) => "Swap", + (25, 3036) => "Roulette", + (25, 3037) => "deck", + (25, 3038) => "desired rule", + (25, 3039) => "GATE", + (25, 3040) => "Cactpot", + (25, 3041) => "Jumbo Cactpot", + (25, 3042) => "Mini Cactpot", + (25, 3043) => "winning number", + (25, 3044) => "Lord of Verminion", + (25, 3045) => "Strengths", + (25, 3046) => "Arcana Stone", + (25, 3047) => "Verminion Gate", + (25, 3048) => "Shield", + (25, 3049) => "Search Eye", + (25, 3050) => "critter", + (25, 3051) => "poppet", + (25, 3052) => "gadget", + (25, 3053) => "ability", + (25, 3054) => "trap", + (25, 3055) => "Any Way the Wind Blows", + (25, 3056) => "Cliffhanger", + (25, 3060) => "Costa del Sol", + (25, 3061) => "monster", + (25, 3062) => "Leap of Faith", + (25, 3063) => "Air Force One", + (25, 3064) => "Doman Mahjong", + (25, 3065) => "The Slice Is Right", + (26, 3300) => "【Statuses】", + (26, 3301) => "paralysis (status)", + (26, 3302) => "silence (status)", + (26, 3303) => "stun (status)", + (26, 3304) => "sleep (status)", + (26, 3305) => "heavy (status)", + (26, 3306) => "petrification (status)", + (26, 3307) => "charmed (status)", + (26, 3308) => "drain (status)", + (26, 3309) => "bind (status)", + (26, 3310) => "transfiguration (status)", + (26, 3311) => "poison (status)", + (25, 3313) => "Draft", + (25, 3314) => "Open Tournament", + (8, 3500) => "", + (8, 3501) => "", + (8, 3502) => "", + (8, 3503) => "", + (8, 3504) => "inner", + (8, 3505) => "outer", + (8, 3506) => "clockwise", + (8, 3507) => "counterclockwise", + (8, 3508) => "offensive stance", + (8, 3509) => "defensive stance", + (8, 3510) => "action indicator", + (8, 3511) => "gaze", + (8, 3512) => "circle", + (8, 3513) => "fan", + (8, 3514) => "diagonal", + (8, 3515) => "straight line", + (8, 3516) => "donut", + (8, 3517) => "line", + (8, 3518) => "sphere", + (8, 3519) => "ground", + (8, 3520) => "wall", + (8, 3521) => "pillar", + (8, 3522) => "boulder", + (8, 3523) => "flame", + (8, 3524) => "water", + (8, 3525) => "ice", + (8, 3526) => "", + (8, 3527) => "explosion", + (8, 3528) => "simultaneously", + (8, 3529) => "one after the other", + (8, 3531) => "combine", + (8, 3535) => "gather", + (8, 3536) => "spread out", + (8, 3540) => "divided damage", + (8, 3541) => "reflected damage", + (8, 3542) => "damage over time", + (8, 3543) => "mode change", + (8, 3544) => "phase", + (8, 3545) => "stacked enhancement", + (8, 3546) => "stacked enfeeblement", + (8, 3547) => "random target", + (27, 3600) => "【Deep Dungeon】", + (27, 3601) => "Cairn of Passage", + (27, 3602) => "Cairn of Return", + (27, 3603) => "Pomander of Safety", + (27, 3604) => "Pomander of Sight", + (27, 3605) => "Pomander of Strength", + (27, 3606) => "Pomander of Steel", + (27, 3607) => "Pomander of Affluence", + (27, 3608) => "Pomander of Flight", + (27, 3609) => "Pomander of Alteration", + (27, 3610) => "Pomander of Purity", + (27, 3611) => "Pomander of Fortune", + (27, 3612) => "Pomander of Witching", + (27, 3613) => "Pomander of Serenity", + (27, 3614) => "Pomander of Rage", + (27, 3615) => "Pomander of Lust", + (27, 3616) => "Pomander of Intuition", + (27, 3617) => "Pomander of Raising", + (27, 3618) => "Pomander of Resolution", + (27, 3619) => "Pomander of Frailty", + (27, 3620) => "Pomander of Concealment", + (27, 3621) => "Pomander of Petrification", + (27, 3622) => "Inferno magicite", + (27, 3623) => "Crag magicite", + (27, 3624) => "Vortex magicite", + (27, 3625) => "elder magicite", + (27, 3626) => "Beacon of Passage", + (27, 3627) => "Beacon of Return", + (17, 4000) => "magia board", + (17, 4001) => "magia melder", + (17, 4002) => "magicite", + (17, 4003) => "aspect value", + (17, 4004) => "elemental level", + (17, 4005) => "logogram", + (17, 4007) => "logos manipulator", + (17, 4008) => "logos action", + (17, 4009) => "Eureka weapon", + (17, 4011) => "custom match", + (17, 4012) => "MGP", + (17, 4013) => "venture", + (17, 4014) => "custom delivery", + (17, 4016) => "Allagan tomestone of phantasmagoria", + (17, 4017) => "Ishgardian Restoration", + (17, 4018) => "Allagan tomestone of allegory", + (17, 4019) => "ocean fishing", + (17, 4020) => "Skysteel tool", + (17, 4021) => "Faux Hollows", + (17, 4022) => "lost action", + (17, 4023) => "lost finds holster", + (17, 4024) => "lost finds cache", + (17, 4025) => "forgotten fragment", + (17, 4026) => "mettle", + (17, 4027) => "Resistance rank", + (17, 4028) => "Explorer Mode", + (17, 4029) => "Allagan tomestone of revelation", + (17, 4030) => "Bozjan cluster", + (17, 4031) => "fashion accessories", + (17, 4032) => "Resistance weapon", + (17, 4033) => "Resistance Recruitment", + (17, 4034) => "Fête", + (24, 5000) => "normal raid", + (24, 5001) => "daily challenge", + (24, 5002) => "The Swallow's Compass", + (24, 5003) => "The Ridorana Lighthouse", + (24, 5004) => "Castrum Fluminis", + (24, 5005) => "The Minstrel's Ballad: Tsukuyomi's Pain", + (24, 5006) => "The Weapon's Refrain (Ultimate)", + (24, 5007) => "Heaven-on-High", + (24, 5008) => "The Great Hunt", + (24, 5009) => "The Great Hunt (Extreme)", + (24, 5010) => "Pagos", + (24, 5011) => "The Sirensong Sea", + (24, 5012) => "Ala Mhigo", + (24, 5013) => "The Minstrel's Ballad: Thordan's Reign", + (24, 5014) => "The Burn", + (24, 5015) => "Hells' Kier", + (24, 5016) => "Hells' Kier (Extreme)", + (24, 5017) => "Alphascape", + (24, 5018) => "Pyros", + (24, 5019) => "Kugane Castle", + (24, 5020) => "The Diadem", + (24, 5021) => "The Ghimlyt Dark", + (24, 5022) => "The Wreath of Snakes", + (24, 5023) => "The Wreath of Snakes (Extreme)", + (24, 5024) => "The Orbonne Monastery", + (24, 5025) => "Hydatos", + (24, 5026) => "Hidden Gorge", + (24, 5027) => "Holminster Switch", + (24, 5028) => "Dohn Mheg", + (24, 5029) => "The Qitana Ravel", + (24, 5030) => "Malikah's Well", + (24, 5031) => "Mt. Gulg", + (24, 5032) => "Amaurot", + (24, 5033) => "The Twinning", + (24, 5034) => "Akadaemia Anyder", + (24, 5035) => "The Dancing Plague", + (24, 5036) => "The Dancing Plague (Extreme)", + (24, 5037) => "The Crown of the Immaculate", + (24, 5038) => "The Crown of the Immaculate (Extreme)", + (24, 5039) => "The Dying Gasp", + (24, 5040) => "Eden", + (24, 5041) => "Eden (Savage)", + (24, 5042) => "Eden's Gate", + (24, 5043) => "Kugane Ohashi", + (24, 5044) => "The Baldesion Arsenal", + (24, 5045) => "The Grand Cosmos", + (24, 5046) => "The Minstrel's Ballad: Hades's Elegy", + (24, 5047) => "The Epic of Alexander (Ultimate)", + (24, 5048) => "The Copied Factory", + (24, 5049) => "Onsal Hakair (Danshig Naadam)", + (24, 5050) => "Anamnesis Anyder", + (24, 5051) => "Eden's Verse", + (24, 5052) => "Cinder Drift", + (24, 5053) => "Cinder Drift (Extreme)", + (24, 5054) => "Memoria Misera (Extreme)", + (24, 5055) => "Bozjan southern front", + (24, 5056) => "The Heroes' Gauntlet", + (24, 5057) => "The Seat of Sacrifice ", + (24, 5058) => "The Seat of Sacrifice (Extreme)", + (24, 5059) => "The Puppets' Bunker", + (24, 5061) => "skirmish", + (24, 5062) => "critical engagement", + (24, 5063) => "Save the Queen", + (24, 5064) => "Matoya's Relict", + (24, 5065) => "Eden's Promise", + (24, 5066) => "Castrum Marinum", + (24, 5067) => "Castrum Marinum (Extreme)", + (24, 5068) => "Delubrum Reginae", + (24, 5070) => "The Battle of Castrum Lacus Litore", + (24, 5071) => "Paglth'an", + (24, 5072) => "The Cloud Deck", + (24, 5073) => "The Cloud Deck (Extreme)", + (24, 5074) => "The Tower at Paradigm's Breach", + (24, 5075) => "The Whorleater (Unreal)", + (24, 5076) => "Zadnor", + (24, 5077) => "The Dalriada", + }; +} diff --git a/src/ffxiv/duties.rs b/src/ffxiv/duties.rs new file mode 100644 index 0000000..eaeffb8 --- /dev/null +++ b/src/ffxiv/duties.rs @@ -0,0 +1,573 @@ +use std::collections::HashMap; + +lazy_static::lazy_static! { + pub static ref DUTIES: HashMap = maplit::hashmap! { + 1 => "The Thousand Maws of TotoRak", + 2 => "The TamTara Deepcroft", + 3 => "Copperbell Mines", + 4 => "Sastasha", + 5 => "The Aurum Vale", + 6 => "Haukke Manor", + 7 => "Halatali", + 8 => "Brayflox's Longstop", + 9 => "The Sunken Temple of Qarn", + 10 => "The Wanderer's Palace", + 11 => "The Stone Vigil", + 12 => "Cutter's Cry", + 13 => "Dzemael Darkhold", + 14 => "Amdapor Keep", + 15 => "Castrum Meridianum", + 16 => "The Praetorium", + 17 => "Pharos Sirius", + 18 => "Copperbell Mines (Hard)", + 19 => "Haukke Manor (Hard)", + 20 => "Brayflox's Longstop (Hard)", + 21 => "Halatali (Hard)", + 22 => "The Lost City of Amdapor", + 23 => "Hullbreaker Isle", + 24 => "The TamTara Deepcroft (Hard)", + 25 => "The Stone Vigil (Hard)", + 26 => "The Sunken Temple of Qarn (Hard)", + 27 => "Snowcloak", + 28 => "Sastasha (Hard)", + 29 => "Amdapor Keep (Hard)", + 30 => "The Wanderer's Palace (Hard)", + 31 => "The Great Gubal Library", + 32 => "The Keeper of the Lake", + 33 => "Neverreap", + 34 => "The Vault", + 35 => "The Fractal Continuum", + 36 => "The Dusk Vigil", + 37 => "Sohm Al", + 38 => "The Aetherochemical Research Facility", + 39 => "The Aery", + 40 => "Pharos Sirius (Hard)", + 41 => "Saint Mocianne's Arboretum", + 42 => "Basic Training: Enemy Parties", + 43 => "Under the Armor", + 44 => "Basic Training: Enemy Strongholds", + 45 => "Hero on the Half Shell", + 46 => "Pulling Poison Posies", + 47 => "Stinging Back", + 48 => "All's Well that Ends in the Well", + 49 => "Flicking Sticks and Taking Names", + 50 => "More than a Feeler", + 51 => "Annoy the Void", + 52 => "Shadow and Claw", + 53 => "Long Live the Queen", + 54 => "Ward Up", + 55 => "Solemn Trinity", + 56 => "The Bowl of Embers", + 57 => "The Navel", + 58 => "The Howling Eye", + 59 => "The Bowl of Embers (Hard)", + 60 => "The Navel (Hard)", + 61 => "The Howling Eye (Hard)", + 62 => "Cape Westwind", + 63 => "The Bowl of Embers (Extreme)", + 64 => "The Navel (Extreme)", + 65 => "The Howling Eye (Extreme)", + 66 => "Thornmarch (Hard)", + 67 => "Thornmarch (Extreme)", + 68 => "The Minstrel's Ballad: Ultima's Bane", + 69 => "Special Event III", + 70 => "Special Event I", + 71 => "Special Event II", + 72 => "The Whorleater (Hard)", + 73 => "The Whorleater (Extreme)", + 74 => "A Relic Reborn: the Chimera", + 75 => "A Relic Reborn: the Hydra", + 76 => "Battle on the Big Bridge", + 77 => "The Striking Tree (Hard)", + 78 => "The Striking Tree (Extreme)", + 79 => "The Akh Afah Amphitheatre (Hard)", + 80 => "The Akh Afah Amphitheatre (Extreme)", + 81 => "The Dragon's Neck", + 82 => "Urth's Fount", + 83 => "The Steps of Faith", + 84 => "The Chrysalis", + 85 => "Battle in the Big Keep", + 86 => "Thok ast Thok (Hard)", + 87 => "Thok ast Thok (Extreme)", + 88 => "The Limitless Blue (Hard)", + 89 => "The Limitless Blue (Extreme)", + 90 => "The Singularity Reactor", + 91 => "The Minstrel's Ballad: Thordan's Reign", + 92 => "The Labyrinth of the Ancients", + 93 => "The Binding Coil of Bahamut - Turn 1", + 94 => "The Binding Coil of Bahamut - Turn 2", + 95 => "The Binding Coil of Bahamut - Turn 3", + 96 => "The Binding Coil of Bahamut - Turn 4", + 97 => "The Binding Coil of Bahamut - Turn 5", + 98 => "The Second Coil of Bahamut - Turn 1", + 99 => "The Second Coil of Bahamut - Turn 2", + 100 => "The Second Coil of Bahamut - Turn 3", + 101 => "The Second Coil of Bahamut - Turn 4", + 102 => "Syrcus Tower", + 103 => "The Second Coil of Bahamut (Savage) - Turn 1", + 104 => "The Second Coil of Bahamut (Savage) - Turn 2", + 105 => "The Second Coil of Bahamut (Savage) - Turn 3", + 106 => "The Second Coil of Bahamut (Savage) - Turn 4", + 107 => "The Final Coil of Bahamut - Turn 1", + 108 => "The Final Coil of Bahamut - Turn 2", + 109 => "The Final Coil of Bahamut - Turn 3", + 110 => "The Final Coil of Bahamut - Turn 4", + 111 => "The World of Darkness", + 112 => "Alexander - The Fist of the Father", + 113 => "Alexander - The Cuff of the Father", + 114 => "Alexander - The Arm of the Father", + 115 => "Alexander - The Burden of the Father", + 116 => "Alexander - The Fist of the Father (Savage)", + 117 => "Alexander - The Cuff of the Father (Savage)", + 118 => "Alexander - The Arm of the Father (Savage)", + 119 => "Alexander - The Burden of the Father (Savage)", + 120 => "The Void Ark", + 127 => "The Borderland Ruins (Secure)", + 130 => "Seal Rock (Seize)", + 131 => "The Diadem (Easy)", + 132 => "The Diadem", + 133 => "The Diadem (Hard)", + 134 => "Containment Bay S1T7", + 135 => "Containment Bay S1T7 (Extreme)", + 136 => "Alexander - The Fist of the Son", + 137 => "Alexander - The Cuff of the Son", + 138 => "Alexander - The Arm of the Son", + 139 => "Alexander - The Burden of the Son", + 140 => "The Lost City of Amdapor (Hard)", + 141 => "The Antitower", + 143 => "The Feast (4 on 4 - Training)", + 145 => "The Feast (4 on 4 - Ranked)", + 147 => "Alexander - The Fist of the Son (Savage)", + 148 => "Alexander - The Cuff of the Son (Savage)", + 149 => "Alexander - The Arm of the Son (Savage)", + 150 => "Alexander - The Burden of the Son (Savage)", + 151 => "Avoid Area of Effect Attacks", + 152 => "Execute a Combo to Increase Enmity", + 153 => "Execute a Combo in Battle", + 154 => "Accrue Enmity from Multiple Targets", + 155 => "Engage Multiple Targets", + 156 => "Execute a Ranged Attack to Increase Enmity", + 157 => "Engage Enemy Reinforcements", + 158 => "Assist Allies in Defeating a Target", + 159 => "Defeat an Occupied Target", + 160 => "Avoid Engaged Targets", + 161 => "Engage Enemy Reinforcements", + 162 => "Interact with the Battlefield", + 163 => "Heal an Ally", + 164 => "Heal Multiple Allies", + 165 => "Avoid Engaged Targets", + 166 => "Final Exercise", + 167 => "A Spectacle for the Ages", + 168 => "The Weeping City of Mhach", + 169 => "The Final Steps of Faith", + 170 => "The Minstrel's Ballad: Nidhogg's Rage", + 171 => "Sohr Khai", + 172 => "Hullbreaker Isle (Hard)", + 173 => "A Bloody Reunion", + 174 => "The Palace of the Dead (Floors 1-10)", + 175 => "The Palace of the Dead (Floors 11-20)", + 176 => "The Palace of the Dead (Floors 21-30)", + 177 => "The Palace of the Dead (Floors 31-40)", + 178 => "The Palace of the Dead (Floors 41-50)", + 179 => "The Aquapolis", + 180 => "The Fields of Glory (Shatter)", + 181 => "The Haunted Manor", + 182 => "Xelphatol", + 183 => "Containment Bay P1T6", + 184 => "Containment Bay P1T6 (Extreme)", + 186 => "Alexander - The Eyes of the Creator", + 187 => "Alexander - The Breath of the Creator", + 188 => "Alexander - The Heart of the Creator", + 189 => "Alexander - The Soul of the Creator", + 190 => "Alexander - The Eyes of the Creator (Savage)", + 191 => "Alexander - The Breath of the Creator (Savage)", + 192 => "Alexander - The Heart of the Creator (Savage)", + 193 => "Alexander - The Soul of the Creator (Savage)", + 194 => "One Life for One World", + 195 => "The Triple Triad Battlehall", + 196 => "The Great Gubal Library (Hard)", + 197 => "LoVM: Player Battle (RP)", + 198 => "LoVM: Tournament", + 199 => "LoVM: Player Battle (Non-RP)", + 201 => "The Feast (Custom Match - Feasting Grounds)", + 202 => "The Diadem Hunting Grounds (Easy)", + 203 => "The Diadem Hunting Grounds", + 204 => "The Palace of the Dead (Floors 51-60)", + 205 => "The Palace of the Dead (Floors 61-70)", + 206 => "The Palace of the Dead (Floors 71-80)", + 207 => "The Palace of the Dead (Floors 81-90)", + 208 => "The Palace of the Dead (Floors 91-100)", + 209 => "The Palace of the Dead (Floors 101-110)", + 210 => "The Palace of the Dead (Floors 111-120)", + 211 => "The Palace of the Dead (Floors 121-130)", + 212 => "The Palace of the Dead (Floors 131-140)", + 213 => "The Palace of the Dead (Floors 141-150)", + 214 => "The Palace of the Dead (Floors 151-160)", + 215 => "The Palace of the Dead (Floors 161-170)", + 216 => "The Palace of the Dead (Floors 171-180)", + 217 => "The Palace of the Dead (Floors 181-190)", + 218 => "The Palace of the Dead (Floors 191-200)", + 219 => "Baelsar's Wall", + 220 => "Dun Scaith", + 221 => "Sohm Al (Hard)", + 222 => "The Carteneau Flats: Heliodrome", + 223 => "Containment Bay Z1T9", + 224 => "Containment Bay Z1T9 (Extreme)", + 225 => "The Diadem - Trials of the Fury", + 228 => "The Feast (4 on 4 - Training)", + 230 => "The Feast (4 on 4 - Ranked)", + 233 => "The Feast (Custom Match - Lichenweed)", + 234 => "The Diadem - Trials of the Matron", + 235 => "Shisui of the Violet Tides", + 236 => "The Temple of the Fist", + 237 => "It's Probably a Trap", + 238 => "The Sirensong Sea", + 239 => "The Royal Menagerie", + 240 => "Bardam's Mettle", + 241 => "Doma Castle", + 242 => "Castrum Abania", + 243 => "The Pool of Tribute", + 244 => "The Pool of Tribute (Extreme)", + 245 => "With Heart and Steel", + 246 => "Naadam", + 247 => "Ala Mhigo", + 248 => "Blood on the Deck", + 249 => "The Face of True Evil", + 250 => "Matsuba Mayhem", + 251 => "The Battle on Bekko", + 252 => "Deltascape V1.0", + 253 => "Deltascape V2.0", + 254 => "Deltascape V3.0", + 255 => "Deltascape V4.0", + 256 => "Deltascape V1.0 (Savage)", + 257 => "Deltascape V2.0 (Savage)", + 258 => "Deltascape V3.0 (Savage)", + 259 => "Deltascape V4.0 (Savage)", + 260 => "Curious Gorge Meets His Match", + 261 => "In Thal's Name", + 262 => "Kugane Castle", + 263 => "Emanation", + 264 => "Emanation (Extreme)", + 265 => "Our Unsung Heroes", + 266 => "The Heart of the Problem", + 267 => "Dark as the Night Sky", + 268 => "The Lost Canals of Uznair", + 269 => "The Resonant", + 270 => "Raising the Sword", + 271 => "The Orphans and the Broken Blade", + 272 => "Our Compromise", + 273 => "Dragon Sound", + 274 => "When Clans Collide", + 275 => "Interdimensional Rift", + 276 => "The Hidden Canals of Uznair", + 277 => "Astragalos", + 278 => "The Minstrel's Ballad: Shinryu's Domain", + 279 => "The Drowned City of Skalla", + 280 => "The Unending Coil of Bahamut (Ultimate)", + 281 => "The Royal City of Rabanastre", + 282 => "Return of the Bull", + 283 => "The Forbidden Land, Eureka Anemos", + 284 => "Hells' Lid", + 285 => "The Fractal Continuum (Hard)", + 286 => "Sigmascape V1.0", + 287 => "Sigmascape V2.0", + 288 => "Sigmascape V3.0", + 289 => "Sigmascape V4.0", + 290 => "The Jade Stoa", + 291 => "The Jade Stoa (Extreme)", + 292 => "Sigmascape V1.0 (Savage)", + 293 => "Sigmascape V2.0 (Savage)", + 294 => "Sigmascape V3.0 (Savage)", + 295 => "Sigmascape V4.0 (Savage)", + 473 => "The Valentione's Ceremony", + 474 => "The Great Hunt", + 475 => "The Great Hunt (Extreme)", + 476 => "The Feast (Team Ranked)", + 478 => "The Feast (Ranked)", + 479 => "The Feast (Training)", + 480 => "The Feast (Custom Match - Crystal Tower)", + 481 => "Chocobo Race: Tutorial", + 482 => "Race 1 - Hugging the Inside", + 483 => "Race 2 - Keep Away", + 484 => "Race 3 - Inability", + 485 => "Race 4 - Heavy Hooves", + 486 => "Race 5 - Defending the Rush", + 487 => "Race 6 - Road Rivals", + 488 => "Race 7 - Field of Dreams", + 489 => "Race 8 - Playing Both Ends", + 490 => "Race 9 - Stamina", + 491 => "Race 10 - Cat and Mouse", + 492 => "Race 11 - Mad Dash", + 493 => "Race 12 - Bag of Tricks", + 494 => "Race 13 - Tag Team", + 495 => "Race 14 - Heavier Hooves", + 496 => "Race 15 - Ultimatum", + 497 => "Chocobo Race: Sagolii Road", + 498 => "Chocobo Race: Costa del Sol", + 499 => "Chocobo Race: Tranquil Paths", + 500 => "Chocobo Race: Sagolii Road", + 501 => "Chocobo Race: Costa del Sol", + 502 => "Chocobo Race: Tranquil Paths", + 503 => "Chocobo Race: Sagolii Road", + 504 => "Chocobo Race: Costa del Sol", + 505 => "Chocobo Race: Tranquil Paths", + 506 => "Chocobo Race: Sagolii Road", + 507 => "Chocobo Race: Costa del Sol", + 508 => "Chocobo Race: Tranquil Paths", + 509 => "Chocobo Race: Sagolii Road", + 510 => "Chocobo Race: Costa del Sol", + 511 => "Chocobo Race: Tranquil Paths", + 512 => "Chocobo Race: Sagolii Road", + 513 => "Chocobo Race: Costa del Sol", + 514 => "Chocobo Race: Tranquil Paths", + 515 => "Chocobo Race: Sagolii Road", + 516 => "Chocobo Race: Costa del Sol", + 517 => "Chocobo Race: Tranquil Paths", + 518 => "Chocobo Race: Sagolii Road", + 519 => "Chocobo Race: Costa del Sol", + 520 => "Chocobo Race: Tranquil Paths", + 521 => "Chocobo Race: Sagolii Road", + 522 => "Chocobo Race: Costa del Sol", + 523 => "Chocobo Race: Tranquil Paths", + 524 => "Chocobo Race: Sagolii Road", + 525 => "Chocobo Race: Costa del Sol", + 526 => "Chocobo Race: Tranquil Paths", + 527 => "Chocobo Race: Sagolii Road", + 528 => "Chocobo Race: Costa del Sol", + 529 => "Chocobo Race: Tranquil Paths", + 530 => "Chocobo Race: Sagolii Road", + 531 => "Chocobo Race: Costa del Sol", + 532 => "Chocobo Race: Tranquil Paths", + 533 => "Chocobo Race: Sagolii Road", + 534 => "Chocobo Race: Costa del Sol", + 535 => "Chocobo Race: Tranquil Paths", + 536 => "The Swallow's Compass", + 537 => "Castrum Fluminis", + 538 => "The Minstrel's Ballad: Tsukuyomi's Pain", + 539 => "The Weapon's Refrain (Ultimate)", + 540 => "Heaven-on-High (Floors 1-10)", + 541 => "Heaven-on-High (Floors 11-20)", + 542 => "Heaven-on-High (Floors 21-30)", + 543 => "Heaven-on-High (Floors 31-40)", + 544 => "Heaven-on-High (Floors 41-50)", + 545 => "Heaven-on-High (Floors 51-60)", + 546 => "Heaven-on-High (Floors 61-70)", + 547 => "Heaven-on-High (Floors 71-80)", + 548 => "Heaven-on-High (Floors 81-90)", + 549 => "Heaven-on-High (Floors 91-100)", + 550 => "The Ridorana Lighthouse", + 552 => "Stage 1: Tutorial", + 553 => "Stage 2: Hatching a Plan", + 554 => "Stage 3: The First Move", + 555 => "Stage 4: Little Big Beast", + 556 => "Stage 5: Turning Tribes", + 557 => "Stage 6: Off the Deepcroft", + 558 => "Stage 7: Rivals", + 559 => "Stage 8: Always Darkest", + 560 => "Stage 9: Mine Your Minions", + 561 => "Stage 10: Children of Mandragora", + 562 => "Stage 11: The Queen and I", + 563 => "Stage 12: Breakout", + 564 => "Stage 13: My Name Is Cid", + 565 => "Stage 14: Like a Nut", + 566 => "Stage 15: Urth's Spout", + 567 => "Stage 16: Exodus", + 568 => "Stage 17: Over the Wall", + 569 => "Stage 18: The Hunt", + 570 => "Stage 19: Battle on the Bitty Bridge", + 571 => "Stage 20: Guiding Light", + 572 => "Stage 21: Wise Words", + 573 => "Stage 22: World of Poor Lighting", + 574 => "Stage 23: The Binding Coil", + 575 => "Stage 24: The Final Coil", + 576 => "LoVM: Master Battle", + 577 => "LoVM: Master Battle (Hard)", + 578 => "LoVM: Master Battle (Extreme)", + 579 => "LoVM: Master Tournament", + 580 => "The Feast (Team Custom Match - Crystal Tower)", + 581 => "The Forbidden Land, Eureka Pagos", + 582 => "Emissary of the Dawn", + 583 => "The Calamity Retold", + 584 => "Saint Mocianne's Arboretum (Hard)", + 585 => "The Burn", + 586 => "The Shifting Altars of Uznair", + 587 => "Alphascape V1.0", + 588 => "Alphascape V2.0", + 589 => "Alphascape V3.0", + 590 => "Alphascape V4.0", + 591 => "Alphascape V1.0 (Savage)", + 592 => "Alphascape V2.0 (Savage)", + 593 => "Alphascape V3.0 (Savage)", + 594 => "Alphascape V4.0 (Savage)", + 595 => "Kugane Ohashi", + 596 => "Hells' Kier", + 597 => "Hells' Kier (Extreme)", + 598 => "The Forbidden Land, Eureka Pyros", + 599 => "Hidden Gorge", + 600 => "Leap of Faith", + 601 => "Leap of Faith", + 602 => "Leap of Faith", + 603 => "Leap of Faith", + 604 => "Leap of Faith", + 605 => "Leap of Faith", + 606 => "Leap of Faith", + 607 => "Leap of Faith", + 608 => "Leap of Faith", + 609 => "The Will of the Moon", + 610 => "All's Well That Starts Well", + 611 => "The Ghimlyt Dark", + 612 => "Much Ado About Pudding", + 613 => "Waiting for Golem", + 614 => "Gentlemen Prefer Swords", + 615 => "The Threepenny Turtles", + 616 => "Eye Society", + 617 => "A Chorus Slime", + 618 => "Bomb-edy of Errors", + 619 => "To Kill a Mockingslime", + 620 => "A Little Knight Music", + 621 => "Some Like It Excruciatingly Hot", + 622 => "The Plant-om of the Opera", + 623 => "Beauty and a Beast", + 624 => "Blobs in the Woods", + 625 => "The Me Nobody Nodes", + 626 => "Sunset Bull-evard", + 627 => "The Sword of Music", + 628 => "Midsummer Night's Explosion", + 629 => "On a Clear Day You Can Smell Forever", + 630 => "Miss Typhon", + 631 => "Chimera on a Hot Tin Roof", + 632 => "Here Comes the Boom", + 633 => "Behemoths and Broomsticks", + 634 => "Amazing Technicolor Pit Fiends", + 635 => "Dirty Rotten Azulmagia", + 636 => "The Orbonne Monastery", + 637 => "The Wreath of Snakes", + 638 => "The Wreath of Snakes (Extreme)", + 639 => "The Forbidden Land, Eureka Hydatos", + 640 => "Air Force One", + 641 => "Air Force One", + 642 => "Air Force One", + 643 => "Novice Mahjong (Full Ranked Match)", + 644 => "Advanced Mahjong (Full Ranked Match)", + 645 => "Four-player Mahjong (Full Match, Kuitan Enabled)", + 646 => "Messenger of the Winds", + 648 => "A Requiem for Heroes", + 649 => "Dohn Mheg", + 650 => "Four-player Mahjong (Full Match, Kuitan Disabled)", + 651 => "The Qitana Ravel", + 652 => "Amaurot", + 653 => "Eden's Gate: Resurrection", + 654 => "Eden's Gate: Resurrection (Savage)", + 655 => "The Twinning", + 656 => "Malikah's Well", + 657 => "The Dancing Plague", + 658 => "The Dancing Plague (Extreme)", + 659 => "Mt. Gulg", + 661 => "Akadaemia Anyder", + 666 => "The Crown of the Immaculate", + 667 => "The Crown of the Immaculate (Extreme)", + 676 => "Holminster Switch", + 678 => "The Hardened Heart", + 679 => "The Lost and the Found", + 680 => "Coming Clean", + 681 => "Legend of the Not-so-hidden Temple", + 682 => "Eden's Gate: Inundation", + 683 => "Eden's Gate: Inundation (Savage)", + 684 => "Eden's Gate: Descent", + 685 => "Eden's Gate: Descent (Savage)", + 686 => "Nyelbert's Lament", + 687 => "The Dying Gasp", + 688 => "The Dungeons of Lyhe Ghiah", + 689 => "Eden's Gate: Sepulture", + 690 => "Eden's Gate: Sepulture (Savage)", + 691 => "The Hunter's Legacy", + 692 => "The Grand Cosmos", + 693 => "The Minstrel's Ballad: Hades's Elegy", + 694 => "The Epic of Alexander (Ultimate)", + 695 => "Papa Mia", + 696 => "Lock up Your Snorters", + 697 => "Dangerous When Dead", + 698 => "Red, Fraught, and Blue", + 699 => "The Catch of the Siegfried", + 700 => "The Copied Factory", + 701 => "Onsal Hakair (Danshig Naadam)", + 702 => "Vows of Virtue, Deeds of Cruelty", + 703 => "As the Heart Bids", + 705 => "Leap of Faith", + 706 => "Leap of Faith", + 707 => "Leap of Faith", + 708 => "Leap of Faith", + 709 => "Leap of Faith", + 710 => "Leap of Faith", + 711 => "Leap of Faith", + 712 => "Leap of Faith", + 713 => "Leap of Faith", + 714 => "Anamnesis Anyder", + 715 => "Eden's Verse: Fulmination", + 716 => "Eden's Verse: Fulmination (Savage)", + 717 => "Cinder Drift", + 718 => "Cinder Drift (Extreme)", + 719 => "Eden's Verse: Furor", + 720 => "Eden's Verse: Furor (Savage)", + 721 => "Ocean Fishing", + 722 => "The Diadem", + 723 => "The Bozja Incident", + 724 => "A Sleep Disturbed", + 725 => "Memoria Misera (Extreme)", + 726 => "Eden's Verse: Iconoclasm", + 727 => "Eden's Verse: Iconoclasm (Savage)", + 728 => "Eden's Verse: Refulgence", + 729 => "Eden's Verse: Refulgence (Savage)", + 730 => "Ocean Fishing", + 731 => "Ocean Fishing", + 732 => "Ocean Fishing", + 733 => "Ocean Fishing", + 734 => "Ocean Fishing", + 735 => "The Bozjan Southern Front", + 736 => "The Puppets' Bunker", + 737 => "The Heroes' Gauntlet", + 738 => "The Seat of Sacrifice", + 739 => "The Seat of Sacrifice (Extreme)", + 740 => "Sleep Now in Sapphire", + 741 => "Sleep Now in Sapphire", + 742 => "The Diadem", + 743 => "Faded Memories", + 745 => "The Shifting Oubliettes of Lyhe Ghiah", + 746 => "Matoya's Relict", + 747 => "Eden's Promise: Litany", + 748 => "Eden's Promise: Litany (Savage)", + 749 => "Eden's Promise: Umbra", + 750 => "Eden's Promise: Umbra (Savage)", + 751 => "Eden's Promise: Anamorphosis", + 752 => "Eden's Promise: Anamorphosis (Savage)", + 753 => "The Diadem", + 754 => "Anything Gogo's", + 755 => "Triple Triad Open Tournament", + 756 => "Triple Triad Invitational Parlor", + 758 => "Eden's Promise: Eternity", + 759 => "Eden's Promise: Eternity (Savage)", + 760 => "Delubrum Reginae", + 761 => "Delubrum Reginae (Savage)", + 762 => "Castrum Marinum", + 763 => "Castrum Marinum (Extreme)", + 764 => "The Great Ship Vylbrand", + 765 => "Fit for a Queen", + 766 => "Novice Mahjong (Quick Ranked Match)", + 767 => "Advanced Mahjong (Quick Ranked Match)", + 768 => "Four-player Mahjong (Quick Match, Kuitan Enabled)", + 769 => "Four-player Mahjong (Quick Match, Kuitan Disabled)", + 770 => "Ocean Fishing", + 771 => "Ocean Fishing", + 772 => "Ocean Fishing", + 773 => "Ocean Fishing", + 774 => "Ocean Fishing", + 775 => "Ocean Fishing", + 776 => "The Whorleater (Unreal)", + 777 => "Paglth'an", + 778 => "Zadnor", + 779 => "The Tower at Paradigm's Breach", + 780 => "Death Unto Dawn", + 781 => "The Cloud Deck", + 782 => "The Cloud Deck (Extreme)", + }; +} diff --git a/src/ffxiv/jobs.rs b/src/ffxiv/jobs.rs new file mode 100644 index 0000000..0d32b50 --- /dev/null +++ b/src/ffxiv/jobs.rs @@ -0,0 +1,45 @@ +use std::collections::HashMap; +use ffxiv_types::jobs::{ClassJob, Class, Job, NonCombatJob}; + +lazy_static::lazy_static! { + pub static ref JOBS: HashMap = maplit::hashmap! { + 1 => ClassJob::Class(Class::Gladiator), + 2 => ClassJob::Class(Class::Pugilist), + 3 => ClassJob::Class(Class::Marauder), + 4 => ClassJob::Class(Class::Lancer), + 5 => ClassJob::Class(Class::Archer), + 6 => ClassJob::Class(Class::Conjurer), + 7 => ClassJob::Class(Class::Thaumaturge), + 8 => ClassJob::NonCombat(NonCombatJob::Carpenter), + 9 => ClassJob::NonCombat(NonCombatJob::Blacksmith), + 10 => ClassJob::NonCombat(NonCombatJob::Armorer), + 11 => ClassJob::NonCombat(NonCombatJob::Goldsmith), + 12 => ClassJob::NonCombat(NonCombatJob::Leatherworker), + 13 => ClassJob::NonCombat(NonCombatJob::Weaver), + 14 => ClassJob::NonCombat(NonCombatJob::Alchemist), + 15 => ClassJob::NonCombat(NonCombatJob::Culinarian), + 16 => ClassJob::NonCombat(NonCombatJob::Miner), + 17 => ClassJob::NonCombat(NonCombatJob::Botanist), + 18 => ClassJob::NonCombat(NonCombatJob::Fisher), + 19 => ClassJob::Job(Job::Paladin), + 20 => ClassJob::Job(Job::Monk), + 21 => ClassJob::Job(Job::Warrior), + 22 => ClassJob::Job(Job::Dragoon), + 23 => ClassJob::Job(Job::Bard), + 24 => ClassJob::Job(Job::WhiteMage), + 25 => ClassJob::Job(Job::BlackMage), + 26 => ClassJob::Class(Class::Arcanist), + 27 => ClassJob::Job(Job::Summoner), + 28 => ClassJob::Job(Job::Scholar), + 29 => ClassJob::Class(Class::Rogue), + 30 => ClassJob::Job(Job::Ninja), + 31 => ClassJob::Job(Job::Machinist), + 32 => ClassJob::Job(Job::DarkKnight), + 33 => ClassJob::Job(Job::Astrologian), + 34 => ClassJob::Job(Job::Samurai), + 35 => ClassJob::Job(Job::RedMage), + 36 => ClassJob::Job(Job::BlueMage), + 37 => ClassJob::Job(Job::Gunbreaker), + 38 => ClassJob::Job(Job::Dancer), + }; +} diff --git a/src/ffxiv/roulettes.rs b/src/ffxiv/roulettes.rs new file mode 100644 index 0000000..777b01e --- /dev/null +++ b/src/ffxiv/roulettes.rs @@ -0,0 +1,41 @@ +use std::collections::HashMap; + +lazy_static::lazy_static! { + pub static ref ROULETTES: HashMap = maplit::hashmap! { + 1 => "Duty Roulette: Leveling", + 2 => "Duty Roulette: Level 50/60/70 Dungeons", + 3 => "Duty Roulette: Main Scenario", + 4 => "Duty Roulette: Guildhests", + 5 => "Duty Roulette: Expert", + 6 => "Duty Roulette: Trials", + 7 => "Daily Challenge: Frontline", + 8 => "Duty Roulette: Level 80 Dungeons", + 9 => "Duty Roulette: Mentor", + 11 => "The Feast (Training Match)", + 13 => "The Feast (Ranked Match)", + 15 => "Duty Roulette: Alliance Raids", + 16 => "The Feast (Team Ranked Match)", + 17 => "Duty Roulette: Normal Raids", + 18 => "Chocobo Race: Sagolii Road", + 19 => "Chocobo Race: Costa del Sol", + 20 => "Chocobo Race: Tranquil Paths", + 21 => "Chocobo Race: Random", + 22 => "Chocobo Race: Sagolii Road (No Rewards)", + 23 => "Chocobo Race: Costa del Sol (No Rewards)", + 24 => "Chocobo Race: Tranquil Paths (No Rewards)", + 25 => "Chocobo Race: Random (No Rewards)", + 26 => "Chocobo Race: Random", + 27 => "Chocobo Race: Random", + 28 => "Chocobo Race: Random", + 29 => "Chocobo Race: Random", + 30 => "Chocobo Race: Random", + 31 => "Chocobo Race: Random", + 32 => "Chocobo Race: Random", + 33 => "Chocobo Race: Random", + 34 => "Chocobo Race: Random", + 35 => "Chocobo Race: Random", + 36 => "Chocobo Race: Random", + 37 => "Chocobo Race: Random", + 38 => "Chocobo Race: Random", + }; +} diff --git a/src/ffxiv/territory_names.rs b/src/ffxiv/territory_names.rs new file mode 100644 index 0000000..45f20a9 --- /dev/null +++ b/src/ffxiv/territory_names.rs @@ -0,0 +1,806 @@ +use std::collections::HashMap; + +lazy_static::lazy_static! { + pub static ref TERRITORY_NAMES: HashMap = maplit::hashmap! { + 128 => "Limsa Lominsa Upper Decks", + 129 => "Limsa Lominsa Lower Decks", + 130 => "Ul'dah - Steps of Nald", + 131 => "Ul'dah - Steps of Thal", + 132 => "New Gridania", + 133 => "Old Gridania", + 134 => "Middle La Noscea", + 135 => "Lower La Noscea", + 136 => "Mist", + 137 => "Eastern La Noscea", + 138 => "Western La Noscea", + 139 => "Upper La Noscea", + 140 => "Western Thanalan", + 141 => "Central Thanalan", + 142 => "Halatali", + 143 => "Steps of Faith", + 144 => "The Gold Saucer", + 145 => "Eastern Thanalan", + 146 => "Southern Thanalan", + 147 => "Northern Thanalan", + 148 => "Central Shroud", + 149 => "The Feasting Grounds", + 150 => "The Keeper of the Lake", + 151 => "The World of Darkness", + 152 => "East Shroud", + 153 => "South Shroud", + 154 => "North Shroud", + 155 => "Coerthas Central Highlands", + 156 => "Mor Dhona", + 157 => "Sastasha", + 158 => "Brayflox's Longstop", + 159 => "The Wanderer's Palace", + 160 => "Pharos Sirius", + 161 => "Copperbell Mines", + 162 => "Halatali", + 163 => "The Sunken Temple of Qarn", + 164 => "The Tam-Tara Deepcroft", + 166 => "Haukke Manor", + 167 => "Amdapor Keep", + 168 => "Stone Vigil", + 169 => "The Thousand Maws of Toto-Rak", + 170 => "Cutter's Cry", + 171 => "Dzemael Darkhold", + 172 => "Aurum Vale", + 174 => "Labyrinth of the Ancients", + 176 => "Mordion Gaol", + 177 => "Mizzenmast Inn", + 178 => "The Hourglass", + 179 => "The Roost", + 180 => "Outer La Noscea", + 181 => "Limsa Lominsa", + 182 => "Ul'dah - Steps of Nald", + 183 => "New Gridania", + 188 => "The Wanderer's Palace", + 189 => "Amdapor Keep", + 190 => "Central Shroud", + 191 => "East Shroud", + 192 => "South Shroud", + 193 => "IC-06 Central Decks", + 194 => "IC-06 Regeneration Grid", + 195 => "IC-06 Main Bridge", + 196 => "The Burning Heart", + 198 => "Command Room", + 202 => "Bowl of Embers", + 204 => "Seat of the First Bow", + 205 => "Lotus Stand", + 206 => "The Navel", + 207 => "Thornmarch", + 208 => "The Howling Eye", + 210 => "Heart of the Sworn", + 212 => "The Waking Sands", + 214 => "Middle La Noscea", + 215 => "Western Thanalan", + 216 => "Central Thanalan", + 217 => "Castrum Meridianum", + 219 => "Central Shroud", + 220 => "South Shroud", + 221 => "Upper La Noscea", + 222 => "Lower La Noscea", + 223 => "Coerthas Central Highlands", + 224 => "The Praetorium", + 225 => "Central Shroud", + 226 => "Central Shroud", + 227 => "Central Shroud", + 228 => "North Shroud", + 229 => "South Shroud", + 230 => "Central Shroud", + 231 => "South Shroud", + 232 => "South Shroud", + 233 => "Central Shroud", + 234 => "East Shroud", + 235 => "South Shroud", + 236 => "South Shroud", + 237 => "Central Shroud", + 238 => "Old Gridania", + 239 => "Central Shroud", + 240 => "North Shroud", + 241 => "Upper Aetheroacoustic Exploratory Site", + 242 => "Lower Aetheroacoustic Exploratory Site", + 243 => "The Ragnarok", + 244 => "Ragnarok Drive Cylinder", + 245 => "Ragnarok Central Core", + 246 => "IC-04 Main Bridge", + 247 => "Ragnarok Main Bridge", + 248 => "Central Thanalan", + 249 => "Lower La Noscea", + 250 => "Wolves' Den Pier", + 251 => "Ul'dah - Steps of Nald", + 252 => "Middle La Noscea", + 253 => "Central Thanalan", + 254 => "Ul'dah - Steps of Nald", + 255 => "Western Thanalan", + 256 => "Eastern Thanalan", + 257 => "Eastern Thanalan", + 258 => "Central Thanalan", + 259 => "Ul'dah - Steps of Nald", + 260 => "Southern Thanalan", + 261 => "Southern Thanalan", + 262 => "Lower La Noscea", + 263 => "Western La Noscea", + 264 => "Lower La Noscea", + 265 => "Lower La Noscea", + 266 => "Eastern Thanalan", + 267 => "Western Thanalan", + 268 => "Eastern Thanalan", + 269 => "Western Thanalan", + 270 => "Central Thanalan", + 271 => "Central Thanalan", + 272 => "Middle La Noscea", + 273 => "Western Thanalan", + 274 => "Ul'dah - Steps of Nald", + 275 => "Eastern Thanalan", + 276 => "Hall of Summoning", + 277 => "East Shroud", + 278 => "Western Thanalan", + 279 => "Lower La Noscea", + 280 => "Western La Noscea", + 281 => "The Whorleater", + 282 => "Private Cottage - Mist", + 283 => "Private House - Mist", + 284 => "Private Mansion - Mist", + 285 => "Middle La Noscea", + 286 => "Rhotano Sea", + 287 => "Lower La Noscea", + 288 => "Rhotano Sea", + 289 => "East Shroud", + 290 => "East Shroud", + 291 => "South Shroud", + 292 => "Bowl of Embers", + 293 => "The Navel", + 294 => "The Howling Eye", + 295 => "Bowl of Embers", + 296 => "The Navel", + 297 => "The Howling Eye", + 298 => "Coerthas Central Highlands", + 299 => "Mor Dhona", + 300 => "Mor Dhona", + 301 => "Coerthas Central Highlands", + 302 => "Coerthas Central Highlands", + 303 => "East Shroud", + 304 => "Coerthas Central Highlands", + 305 => "Mor Dhona", + 306 => "Southern Thanalan", + 307 => "Lower La Noscea", + 308 => "Mor Dhona", + 309 => "Mor Dhona", + 310 => "Eastern La Noscea", + 311 => "Eastern La Noscea", + 312 => "Southern Thanalan", + 313 => "Coerthas Central Highlands", + 314 => "Central Thanalan", + 315 => "Mor Dhona", + 316 => "Coerthas Central Highlands", + 317 => "South Shroud", + 318 => "Southern Thanalan", + 319 => "Central Shroud", + 320 => "Central Shroud", + 321 => "North Shroud", + 322 => "Coerthas Central Highlands", + 323 => "Southern Thanalan", + 324 => "North Shroud", + 325 => "Outer La Noscea", + 326 => "Mor Dhona", + 327 => "Eastern La Noscea", + 328 => "Upper La Noscea", + 329 => "The Wanderer's Palace", + 330 => "Western La Noscea", + 331 => "The Howling Eye", + 332 => "Western Thanalan", + 335 => "Mor Dhona", + 338 => "Eorzean Subterrane", + 339 => "Mist", + 340 => "The Lavender Beds", + 341 => "The Goblet", + 342 => "Private Cottage - The Lavender Beds", + 343 => "Private House - The Lavender Beds", + 344 => "Private Mansion - The Lavender Beds", + 345 => "Private Cottage - The Goblet", + 346 => "Private House - The Goblet", + 347 => "Private Mansion - The Goblet", + 348 => "Porta Decumana", + 349 => "Copperbell Mines", + 350 => "Haukke Manor", + 351 => "The Rising Stones", + 353 => "Kugane Ohashi", + 354 => "The Dancing Plague", + 355 => "Dalamud's Shadow", + 356 => "The Outer Coil", + 357 => "Central Decks", + 358 => "The Holocharts", + 359 => "The Whorleater", + 360 => "Halatali", + 361 => "Hullbreaker Isle", + 362 => "Brayflox's Longstop", + 363 => "The Lost City of Amdapor", + 364 => "Thornmarch", + 365 => "Stone Vigil", + 366 => "Griffin Crossing", + 367 => "The Sunken Temple of Qarn", + 368 => "The Weeping Saint", + 369 => "Hall of the Bestiarii", + 370 => "Main Bridge", + 371 => "Snowcloak", + 372 => "Syrcus Tower", + 373 => "The Tam-Tara Deepcroft", + 374 => "The Striking Tree", + 375 => "The Striking Tree", + 376 => "Carteneau Flats: Borderland Ruins", + 377 => "Akh Afah Amphitheatre", + 378 => "Akh Afah Amphitheatre", + 379 => "Mor Dhona", + 380 => "Dalamud's Shadow", + 381 => "The Outer Coil", + 382 => "Central Decks", + 383 => "The Holocharts", + 384 => "Private Chambers - Mist", + 385 => "Private Chambers - The Lavender Beds", + 386 => "Private Chambers - The Goblet", + 387 => "Sastasha", + 388 => "Chocobo Square", + 389 => "Chocobo Square", + 390 => "Chocobo Square", + 391 => "Chocobo Square", + 392 => "Sanctum of the Twelve", + 393 => "Sanctum of the Twelve", + 394 => "South Shroud", + 395 => "Intercessory", + 396 => "Amdapor Keep", + 397 => "Coerthas Western Highlands", + 398 => "The Dravanian Forelands", + 399 => "The Dravanian Hinterlands", + 400 => "The Churning Mists", + 401 => "The Sea of Clouds", + 402 => "Azys Lla", + 403 => "Ala Mhigo", + 404 => "Limsa Lominsa Lower Decks", + 405 => "Western La Noscea", + 406 => "Western La Noscea", + 407 => "Rhotano Sea", + 408 => "Eastern La Noscea", + 409 => "Limsa Lominsa Upper Decks", + 410 => "Northern Thanalan", + 411 => "Eastern La Noscea", + 412 => "Upper La Noscea", + 413 => "Western La Noscea", + 414 => "Eastern La Noscea", + 415 => "Lower La Noscea", + 416 => "The Great Gubal Library", + 417 => "Chocobo Square", + 418 => "Foundation", + 419 => "The Pillars", + 420 => "Neverreap", + 421 => "The Vault", + 423 => "Company Workshop - Mist", + 424 => "Company Workshop - The Goblet", + 425 => "Company Workshop - The Lavender Beds", + 426 => "The Chrysalis", + 427 => "Saint Endalim's Scholasticate", + 428 => "Seat of the Lord Commander", + 429 => "Cloud Nine", + 430 => "The Fractal Continuum", + 431 => "Seal Rock", + 432 => "Thok ast Thok", + 433 => "Fortemps Manor", + 434 => "Dusk Vigil", + 435 => "The Aery", + 436 => "The Limitless Blue", + 437 => "Singularity Reactor", + 438 => "Aetherochemical Research Facility", + 439 => "The Lightfeather Proving Grounds", + 440 => "Ruling Chamber", + 441 => "Sohm Al", + 442 => "The Fist of the Father", + 443 => "The Cuff of the Father", + 444 => "The Arm of the Father", + 445 => "The Burden of the Father", + 446 => "Thok ast Thok", + 447 => "The Limitless Blue", + 448 => "Singularity Reactor", + 449 => "The Fist of the Father", + 450 => "The Cuff of the Father", + 451 => "The Arm of the Father", + 452 => "The Burden of the Father", + 453 => "Western La Noscea", + 454 => "Upper La Noscea", + 455 => "The Sea of Clouds", + 456 => "Ruling Chamber", + 457 => "Akh Afah Amphitheatre", + 458 => "Foundation", + 459 => "Azys Lla", + 460 => "Halatali", + 461 => "The Sea of Clouds", + 462 => "Sacrificial Chamber", + 463 => "Matoya's Cave", + 464 => "The Dravanian Forelands", + 465 => "Eastern Thanalan", + 466 => "Upper La Noscea", + 467 => "Coerthas Western Highlands", + 468 => "Coerthas Central Highlands", + 469 => "Coerthas Central Highlands", + 470 => "Coerthas Western Highlands", + 471 => "Eastern La Noscea", + 472 => "Coerthas Western Highlands", + 473 => "South Shroud", + 474 => "Limsa Lominsa Upper Decks", + 475 => "Coerthas Central Highlands", + 476 => "The Dravanian Hinterlands", + 477 => "Coerthas Western Highlands", + 478 => "Idyllshire", + 479 => "Coerthas Western Highlands", + 480 => "Mor Dhona", + 481 => "The Dravanian Forelands", + 482 => "The Dravanian Forelands", + 483 => "Northern Thanalan", + 484 => "Lower La Noscea", + 485 => "The Dravanian Hinterlands", + 486 => "Outer La Noscea", + 487 => "Coerthas Central Highlands", + 488 => "Coerthas Central Highlands", + 489 => "Coerthas Western Highlands", + 490 => "Hullbreaker Isle", + 491 => "Southern Thanalan", + 492 => "The Sea of Clouds", + 493 => "Coerthas Western Highlands", + 494 => "Eastern Thanalan", + 495 => "Lower La Noscea", + 496 => "Coerthas Central Highlands", + 497 => "Coerthas Western Highlands", + 498 => "Coerthas Western Highlands", + 499 => "The Pillars", + 500 => "Coerthas Central Highlands", + 501 => "The Churning Mists", + 502 => "Carteneau Flats: Borderland Ruins", + 503 => "The Dravanian Hinterlands", + 504 => "The Eighteenth Floor", + 505 => "Alexander", + 506 => "Chocobo Square", + 507 => "Central Azys Lla", + 508 => "Void Ark", + 509 => "The Navel", + 510 => "Pharos Sirius", + 511 => "Saint Mocianne's Arboretum", + 512 => "The Diadem", + 513 => "The Vault", + 514 => "The Diadem", + 515 => "The Diadem", + 516 => "The Antitower", + 517 => "Containment Bay S1T7", + 519 => "The Lost City of Amdapor", + 520 => "The Fist of the Son", + 521 => "The Cuff of the Son", + 522 => "The Arm of the Son", + 523 => "The Burden of the Son", + 524 => "Containment Bay S1T7", + 525 => "The Feasting Grounds", + 527 => "The Feasting Grounds", + 529 => "The Fist of the Son", + 530 => "The Cuff of the Son", + 531 => "The Arm of the Son", + 532 => "The Burden of the Son", + 533 => "Coerthas Central Highlands", + 534 => "Twin Adder Barracks", + 535 => "Flame Barracks", + 536 => "Maelstrom Barracks", + 537 => "The Fold", + 538 => "The Fold", + 539 => "The Fold", + 540 => "The Fold", + 541 => "The Fold", + 542 => "The Fold", + 543 => "The Fold", + 544 => "The Fold", + 545 => "The Fold", + 546 => "The Fold", + 547 => "The Fold", + 548 => "The Fold", + 549 => "The Fold", + 550 => "The Fold", + 551 => "The Fold", + 552 => "Western La Noscea", + 553 => "Alexander", + 554 => "The Fields of Glory", + 555 => "Sohr Khai", + 556 => "The Weeping City of Mhach", + 557 => "Hullbreaker Isle", + 558 => "The Aquapolis", + 559 => "Steps of Faith", + 560 => "Aetherochemical Research Facility", + 561 => "The Palace of the Dead", + 562 => "The Palace of the Dead", + 563 => "The Palace of the Dead", + 564 => "The Palace of the Dead", + 565 => "The Palace of the Dead", + 566 => "Steps of Faith", + 567 => "The Parrock", + 568 => "Leofard's Chambers", + 569 => "Steps of Faith", + 570 => "The Palace of the Dead", + 571 => "Haunted Manor", + 572 => "Xelphatol", + 573 => "Topmast Apartment Lobby", + 574 => "Lily Hills Apartment Lobby", + 575 => "Sultana's Breath Apartment Lobby", + 576 => "Containment Bay P1T6", + 577 => "Containment Bay P1T6", + 578 => "The Great Gubal Library", + 579 => "The Battlehall", + 580 => "Eyes of the Creator", + 581 => "Breath of the Creator", + 582 => "Heart of the Creator", + 583 => "Soul of the Creator", + 584 => "Eyes of the Creator", + 585 => "Breath of the Creator", + 586 => "Heart of the Creator", + 587 => "Soul of the Creator", + 588 => "Heart of the Creator", + 589 => "Chocobo Square", + 590 => "Chocobo Square", + 591 => "Chocobo Square", + 592 => "Bowl of Embers", + 593 => "The Palace of the Dead", + 594 => "The Palace of the Dead", + 595 => "The Palace of the Dead", + 596 => "The Palace of the Dead", + 597 => "The Palace of the Dead", + 598 => "The Palace of the Dead", + 599 => "The Palace of the Dead", + 600 => "The Palace of the Dead", + 601 => "The Palace of the Dead", + 602 => "The Palace of the Dead", + 603 => "The Palace of the Dead", + 604 => "The Palace of the Dead", + 605 => "The Palace of the Dead", + 606 => "The Palace of the Dead", + 607 => "The Palace of the Dead", + 608 => "Topmast Apartment", + 609 => "Lily Hills Apartment", + 610 => "Sultana's Breath Apartment", + 611 => "Frondale's Home for Friendless Foundlings", + 612 => "The Fringes", + 613 => "The Ruby Sea", + 614 => "Yanxia", + 615 => "Baelsar's Wall", + 616 => "Shisui of the Violet Tides", + 617 => "Sohm Al", + 619 => "The Feasting Grounds", + 620 => "The Peaks", + 621 => "The Lochs", + 622 => "The Azim Steppe", + 623 => "Bardam's Mettle", + 624 => "The Diadem", + 625 => "The Diadem", + 626 => "The Sirensong Sea", + 627 => "Dun Scaith", + 628 => "Kugane", + 629 => "Bokairo Inn", + 630 => "The Diadem", + 632 => "Lichenweed", + 633 => "Carteneau Flats: Borderland Ruins", + 634 => "Yanxia", + 635 => "Rhalgr's Reach", + 636 => "Omega Control", + 637 => "Containment Bay Z1T9", + 638 => "Containment Bay Z1T9", + 639 => "Ruby Bazaar Offices", + 640 => "The Fringes", + 641 => "Shirogane", + 644 => "Lichenweed", + 646 => "Lichenweed", + 647 => "The Fringes", + 648 => "The Fringes", + 649 => "Private Cottage - Shirogane", + 650 => "Private House - Shirogane", + 651 => "Private Mansion - Shirogane", + 652 => "Private Chambers - Shirogane", + 653 => "Company Workshop - Shirogane", + 654 => "Kobai Goten Apartment Lobby", + 655 => "Kobai Goten Apartment", + 656 => "The Diadem", + 657 => "The Ruby Sea", + 658 => "The Interdimensional Rift", + 659 => "Rhalgr's Reach", + 660 => "Doma Castle", + 661 => "Castrum Abania", + 662 => "Kugane Castle", + 663 => "The Temple of the Fist", + 664 => "Kugane", + 665 => "Kugane", + 666 => "Ul'dah - Steps of Thal", + 667 => "Kugane", + 668 => "Eastern Thanalan", + 669 => "Southern Thanalan", + 670 => "The Fringes", + 671 => "The Fringes", + 672 => "Mor Dhona", + 673 => "Sohm Al", + 674 => "The Blessed Treasury", + 675 => "Western La Noscea", + 676 => "The Great Gubal Library", + 677 => "The Blessed Treasury", + 678 => "The Fringes", + 679 => "The Royal Airship Landing", + 680 => "The Misery", + 681 => "The House of the Fierce", + 682 => "The Doman Enclave", + 683 => "The First Altar of Djanan Qhat", + 684 => "The Lochs", + 685 => "Yanxia", + 686 => "The Lochs", + 687 => "The Lochs", + 688 => "The Azim Steppe", + 689 => "Ala Mhigo", + 690 => "The Interdimensional Rift", + 691 => "Deltascape V1.0", + 692 => "Deltascape V2.0", + 693 => "Deltascape V3.0", + 694 => "Deltascape V4.0", + 695 => "Deltascape V1.0", + 696 => "Deltascape V2.0", + 697 => "Deltascape V3.0", + 698 => "Deltascape V4.0", + 699 => "Coerthas Central Highlands", + 700 => "Foundation", + 701 => "Seal Rock", + 702 => "Aetherochemical Research Facility", + 703 => "The Fringes", + 704 => "Dalamud's Shadow", + 705 => "Ul'dah - Steps of Thal", + 706 => "Ul'dah - Steps of Thal", + 707 => "The Weeping City of Mhach", + 708 => "Rhotano Sea", + 709 => "Coerthas Western Highlands", + 710 => "Kugane", + 711 => "The Ruby Sea", + 712 => "The Lost Canals of Uznair", + 713 => "The Azim Steppe", + 714 => "Bardam's Mettle", + 715 => "The Churning Mists", + 716 => "The Peaks", + 717 => "Wolves' Den Pier", + 718 => "The Azim Steppe", + 719 => "Emanation", + 720 => "Emanation", + 721 => "Amdapor Keep", + 722 => "The Lost City of Amdapor", + 723 => "The Azim Steppe", + 724 => "The Interdimensional Rift", + 725 => "The Lost Canals of Uznair", + 726 => "The Ruby Sea", + 727 => "The Royal Menagerie", + 728 => "Mordion Gaol", + 729 => "Astragalos", + 730 => "Transparency", + 731 => "The Drowned City of Skalla", + 732 => "Eureka Anemos", + 733 => "The Binding Coil of Bahamut", + 734 => "The Royal City of Rabanastre", + 735 => "The Prima Vista Tiring Room", + 736 => "The Prima Vista Bridge", + 737 => "Royal Palace", + 738 => "The Resonatorium", + 739 => "The Doman Enclave", + 740 => "The Royal Menagerie", + 741 => "Sanctum of the Twelve", + 742 => "Hells' Lid", + 743 => "The Fractal Continuum", + 744 => "Kienkan", + 745 => "Crystal Tower Training Grounds", + 746 => "The Jade Stoa", + 748 => "Sigmascape V1.0", + 749 => "Sigmascape V2.0", + 750 => "Sigmascape V3.0", + 751 => "Sigmascape V4.0", + 752 => "Sigmascape V1.0", + 753 => "Sigmascape V2.0", + 754 => "Sigmascape V3.0", + 755 => "Sigmascape V4.0", + 756 => "The Interdimensional Rift", + 757 => "The Ruby Sea", + 758 => "The Jade Stoa", + 759 => "The Doman Enclave", + 760 => "The Fringes", + 761 => "The Great Hunt", + 762 => "The Great Hunt", + 763 => "Eureka Pagos", + 764 => "Reisen Temple", + 765 => "Crystal Tower Training Grounds", + 766 => "Crystal Tower Training Grounds", + 767 => "Crystal Tower Training Grounds", + 768 => "The Swallow's Compass", + 769 => "The Burn", + 770 => "Heaven-on-High", + 771 => "Heaven-on-High", + 772 => "Heaven-on-High", + 773 => "Heaven-on-High", + 774 => "Heaven-on-High", + 775 => "Heaven-on-High", + 776 => "The Ridorana Lighthouse", + 777 => "Ultimacy", + 778 => "Castrum Fluminis", + 779 => "Castrum Fluminis", + 780 => "Heaven-on-High", + 781 => "Reisen Temple Road", + 782 => "Heaven-on-High", + 783 => "Heaven-on-High", + 784 => "Heaven-on-High", + 785 => "Heaven-on-High", + 786 => "Castrum Fluminis", + 787 => "The Ridorana Cataract", + 788 => "Saint Mocianne's Arboretum", + 789 => "The Burn", + 790 => "Ul'dah - Steps of Nald", + 791 => "Hidden Gorge", + 792 => "The Fall of Belah'dia", + 793 => "The Ghimlyt Dark", + 794 => "The Shifting Altars of Uznair", + 795 => "Eureka Pyros", + 796 => "Blue Sky", + 797 => "The Azim Steppe", + 798 => "Psiscape V1.0", + 799 => "Psiscape V2.0", + 800 => "The Interdimensional Rift", + 801 => "The Interdimensional Rift", + 802 => "Psiscape V1.0", + 803 => "Psiscape V2.0", + 804 => "The Interdimensional Rift", + 805 => "The Interdimensional Rift", + 806 => "Kugane Ohashi", + 807 => "The Interdimensional Rift", + 808 => "The Interdimensional Rift", + 809 => "Haunted Manor", + 810 => "Hells' Kier", + 811 => "Hells' Kier", + 812 => "The Interdimensional Rift", + 813 => "Lakeland", + 814 => "Kholusia", + 815 => "Amh Araeng", + 816 => "Il Mheg", + 817 => "The Rak'tika Greatwood", + 818 => "The Tempest", + 819 => "The Crystarium", + 820 => "Eulmore", + 821 => "Dohn Mheg", + 822 => "Mt. Gulg", + 823 => "The Qitana Ravel", + 824 => "The Wreath of Snakes", + 825 => "The Wreath of Snakes", + 826 => "The Orbonne Monastery", + 827 => "Eureka Hydatos", + 828 => "The Prima Vista Tiring Room", + 829 => "Eorzean Alliance Headquarters", + 830 => "The Ghimlyt Dark", + 831 => "The Manderville Tables", + 832 => "The Gold Saucer", + 833 => "The Howling Eye", + 834 => "The Howling Eye", + 836 => "Malikah's Well", + 837 => "Holminster Switch", + 838 => "Amaurot", + 839 => "East Shroud", + 840 => "The Twinning", + 841 => "Akadaemia Anyder", + 842 => "The Syrcus Trench", + 843 => "The Pendants Personal Suite", + 844 => "The Ocular", + 845 => "The Dancing Plague", + 846 => "The Crown of the Immaculate", + 847 => "The Dying Gasp", + 848 => "The Crown of the Immaculate", + 849 => "The Core", + 850 => "The Halo", + 851 => "The Nereus Trench", + 852 => "Atlas Peak", + 853 => "The Core", + 854 => "The Halo", + 855 => "The Nereus Trench", + 856 => "Atlas Peak", + 857 => "The Core", + 858 => "The Dancing Plague", + 859 => "The Confessional of Toupasa the Elder", + 860 => "Amh Araeng", + 861 => "Lakeland", + 862 => "Lakeland", + 863 => "Eulmore", + 864 => "Kholusia", + 865 => "Old Gridania", + 866 => "Coerthas Western Highlands", + 867 => "Eastern La Noscea", + 868 => "The Peaks", + 869 => "Il Mheg", + 870 => "Kholusia", + 871 => "The Rak'tika Greatwood", + 872 => "Amh Araeng", + 873 => "The Dancing Plague", + 874 => "The Rak'tika Greatwood", + 875 => "The Rak'tika Greatwood", + 876 => "The Nabaath Mines", + 877 => "Lakeland", + 878 => "The Empty", + 879 => "The Dungeons of Lyhe Ghiah", + 880 => "The Crown of the Immaculate", + 881 => "The Dying Gasp", + 882 => "The Copied Factory", + 884 => "The Grand Cosmos", + 885 => "The Dying Gasp", + 886 => "The Firmament", + 887 => "Liminal Space", + 888 => "Onsal Hakair", + 889 => "Lyhe Mheg", + 890 => "Lyhe Mheg", + 891 => "Lyhe Mheg", + 892 => "Lyhe Mheg", + 893 => "The Imperial Palace", + 894 => "Lyhe Mheg", + 895 => "Excavation Tunnels", + 896 => "The Copied Factory", + 897 => "Cinder Drift", + 898 => "Anamnesis Anyder", + 899 => "The Falling City of Nym", + 900 => "The Endeavor", + 901 => "The Diadem", + 902 => "The Gandof Thunder Plains", + 903 => "Ashfall", + 904 => "The Halo", + 905 => "Great Glacier", + 906 => "The Gandof Thunder Plains", + 907 => "Ashfall", + 908 => "The Halo", + 909 => "Great Glacier", + 911 => "Cid's Memory", + 912 => "Cinder Drift", + 913 => "Transmission Control", + 914 => "Trial's Threshold", + 915 => "Gangos", + 916 => "The Heroes' Gauntlet", + 917 => "The Puppets' Bunker", + 918 => "Anamnesis Anyder", + 919 => "Terncliff", + 920 => "Bozjan Southern Front", + 921 => "Frondale's Home for Friendless Foundlings", + 922 => "The Seat of Sacrifice", + 923 => "The Seat of Sacrifice", + 924 => "The Shifting Oubliettes of Lyhe Ghiah", + 925 => "Terncliff Bay", + 926 => "Terncliff Bay", + 928 => "The Puppets' Bunker", + 929 => "The Diadem", + 930 => "Akh Afah Amphitheatre", + 931 => "The Seat of Sacrifice", + 932 => "The Tempest", + 933 => "Matoya's Relict", + 934 => "Castrum Marinum Drydocks", + 935 => "Castrum Marinum Drydocks", + 936 => "Delubrum Reginae", + 937 => "Delubrum Reginae", + 938 => "Paglth'an", + 939 => "The Diadem", + 940 => "The Battlehall", + 941 => "The Battlehall", + 942 => "Sphere of Naught", + 943 => "Laxan Loft", + 944 => "Bygone Gaol", + 945 => "The Garden of Nowhere", + 946 => "Sphere of Naught", + 947 => "Laxan Loft", + 948 => "Bygone Gaol", + 949 => "The Garden of Nowhere", + 950 => "G-Savior Deck", + 951 => "G-Savior Deck", + 953 => "The Navel", + 954 => "The Navel", + 955 => "The Last Trace", + 964 => "The Last Trace", + 965 => "The Empty", + 966 => "The Tower at Paradigm's Breach", + 967 => "Castrum Marinum Drydocks", + 972 => "The Whorleater", + 975 => "Zadnor", + 977 => "Carteneau Flats: Borderland Ruins", + 991 => "G-Savior Deck", + }; +} diff --git a/src/ffxiv/worlds.rs b/src/ffxiv/worlds.rs new file mode 100644 index 0000000..f0b9804 --- /dev/null +++ b/src/ffxiv/worlds.rs @@ -0,0 +1,75 @@ +use std::collections::HashMap; +use ffxiv_types::World; + +lazy_static::lazy_static! { + pub static ref WORLDS: HashMap = maplit::hashmap! { + 23 => World::Asura, + 24 => World::Belias, + 28 => World::Pandaemonium, + 29 => World::Shinryu, + 30 => World::Unicorn, + 31 => World::Yojimbo, + 32 => World::Zeromus, + 33 => World::Twintania, + 34 => World::Brynhildr, + 35 => World::Famfrit, + 36 => World::Lich, + 37 => World::Mateus, + 39 => World::Omega, + 40 => World::Jenova, + 41 => World::Zalera, + 42 => World::Zodiark, + 43 => World::Alexander, + 44 => World::Anima, + 45 => World::Carbuncle, + 46 => World::Fenrir, + 47 => World::Hades, + 48 => World::Ixion, + 49 => World::Kujata, + 50 => World::Typhon, + 51 => World::Ultima, + 52 => World::Valefor, + 53 => World::Exodus, + 54 => World::Faerie, + 55 => World::Lamia, + 56 => World::Phoenix, + 57 => World::Siren, + 58 => World::Garuda, + 59 => World::Ifrit, + 60 => World::Ramuh, + 61 => World::Titan, + 62 => World::Diabolos, + 63 => World::Gilgamesh, + 64 => World::Leviathan, + 65 => World::Midgardsormr, + 66 => World::Odin, + 67 => World::Shiva, + 68 => World::Atomos, + 69 => World::Bahamut, + 70 => World::Chocobo, + 71 => World::Moogle, + 72 => World::Tonberry, + 73 => World::Adamantoise, + 74 => World::Coeurl, + 75 => World::Malboro, + 76 => World::Tiamat, + 77 => World::Ultros, + 78 => World::Behemoth, + 79 => World::Cactuar, + 80 => World::Cerberus, + 81 => World::Goblin, + 82 => World::Mandragora, + 83 => World::Louisoix, + 85 => World::Spriggan, + 90 => World::Aegis, + 91 => World::Balmung, + 92 => World::Durandal, + 93 => World::Excalibur, + 94 => World::Gungnir, + 95 => World::Hyperion, + 96 => World::Masamune, + 97 => World::Ragnarok, + 98 => World::Ridill, + 99 => World::Sargatanas, + }; +} diff --git a/src/listing.rs b/src/listing.rs new file mode 100644 index 0000000..4616206 --- /dev/null +++ b/src/listing.rs @@ -0,0 +1,375 @@ +use std::borrow::Cow; +use bitflags::bitflags; +use ffxiv_types::jobs::{ClassJob, Class, Job}; +use ffxiv_types::{Role, World}; +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; +use sestring::SeString; + +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct PartyFinderListing { + pub id: u32, + pub content_id_lower: u32, + #[serde(with = "crate::base64_sestring")] + pub name: SeString, + #[serde(with = "crate::base64_sestring")] + pub description: SeString, + pub created_world: u8, + pub home_world: u8, + pub current_world: u8, + pub category: DutyCategory, + pub duty: u16, + pub duty_type: DutyType, + pub beginners_welcome: bool, + pub seconds_remaining: u16, + pub min_item_level: u16, + pub num_parties: u8, + pub slots_available: u8, + pub objective: ObjectiveFlags, + pub conditions: ConditionFlags, + pub duty_finder_settings: DutyFinderSettingsFlags, + pub loot_rules: LootRuleFlags, + pub search_area: SearchAreaFlags, + pub slots: Vec, + pub jobs_present: Vec, +} + +impl PartyFinderListing { + pub fn slots_filled(&self) -> usize { + self.jobs_present.iter().filter(|&&job| job > 0).count() + } + + pub fn is_cross_world(&self) -> bool { + self.search_area.contains(SearchAreaFlags::DATA_CENTRE) + } + + pub fn duty_name(&self) -> Cow { + match (&self.duty_type, &self.category) { + (DutyType::Other, DutyCategory::Fates) => { + if let Some(&name) = crate::ffxiv::TERRITORY_NAMES.get(&u32::from(self.duty)) { + return Cow::from(name); + } + + return Cow::from("Fates"); + } + (DutyType::Other, DutyCategory::TheHunt) => return Cow::from("The Hunt"), + (DutyType::Other, DutyCategory::Duty) if self.duty == 0 => return Cow::from("None"), + (DutyType::Normal, _) => { + if let Some(&name) = crate::ffxiv::DUTIES.get(&u32::from(self.duty)) { + return Cow::from(name); + } + } + (DutyType::Roulette, _) => { + if let Some(&name) = crate::ffxiv::ROULETTES.get(&u32::from(self.duty)) { + return Cow::from(name); + } + } + _ => {} + } + + Cow::from(format!("{:?}", self.category)) + } + + pub fn slots(&self) -> Vec> { + let mut slots = Vec::with_capacity(self.slots_available as usize); + for i in 0..self.slots_available as usize { + if i >= self.jobs_present.len() { + break; + } + + let cj = match crate::ffxiv::JOBS.get(&u32::from(self.jobs_present[i])).copied() { + Some(cj) => Ok(cj), + None => Err(( + self.slots[i].html_classes(), + self.slots[i].codes(), + )), + }; + slots.push(cj); + } + + slots + } + + pub fn created_world(&self) -> Option { + crate::ffxiv::WORLDS.get(&u32::from(self.created_world)).copied() + } + + pub fn created_world_string(&self) -> Cow { + self.created_world() + .map(|world| Cow::from(world.name())) + .unwrap_or_else(|| Cow::from(self.created_world.to_string())) + } + + pub fn home_world(&self) -> Option { + crate::ffxiv::WORLDS.get(&u32::from(self.home_world)).copied() + } + + pub fn home_world_string(&self) -> Cow { + self.home_world() + .map(|world| Cow::from(world.name())) + .unwrap_or_else(|| Cow::from(self.home_world.to_string())) + } +} + +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct PartyFinderSlot { + pub accepting: JobFlags, +} + +impl PartyFinderSlot { + pub fn html_classes(&self) -> String { + if self.accepting == JobFlags::all() { + return "empty".into(); + } + + let mut classes = Vec::with_capacity(3); + let cjs = self.accepting.classjobs(); + + if cjs.iter().any(|cj| cj.role() == Some(Role::Healer)) { + classes.push("healer"); + } + + if cjs.iter().any(|cj| cj.role() == Some(Role::Tank)) { + classes.push("tank"); + } + + if cjs.iter().any(|cj| cj.role() == Some(Role::Dps)) { + classes.push("dps"); + } + + classes.join(" ") + } + + pub fn codes(&self) -> String { + self.accepting.classjobs() + .iter() + .map(|cj| cj.code()) + .intersperse(" ") + .collect() + } +} + +#[derive(Debug, Deserialize_repr, Serialize_repr, PartialEq)] +#[repr(u32)] +pub enum DutyCategory { + Duty = 0, + QuestBattles = 1 << 0, + Fates = 1 << 1, + TreasureHunt = 1 << 2, + TheHunt = 1 << 3, + GatheringForays = 1 << 4, + DeepDungeons = 1 << 5, + AdventuringForays = 1 << 6, +} + +#[derive(Debug, Deserialize_repr, Serialize_repr, PartialEq)] +#[repr(u8)] +pub enum DutyType { + Other = 0, + Roulette = 1 << 0, + Normal = 1 << 1, +} + +bitflags! { + #[derive(Deserialize, Serialize)] + #[serde(transparent)] + pub struct ObjectiveFlags : u32 { + const NONE = 0; + const DUTY_COMPLETION = 1 << 0; + const PRACTICE = 1 << 1; + const LOOT = 1 << 2; + } +} + +bitflags! { + #[derive(Deserialize, Serialize)] + #[serde(transparent)] + pub struct ConditionFlags : u32 { + const NONE = 1 << 0; + const DUTY_COMPLETE = 1 << 1; + const DUTY_INCOMPLETE = 1 << 2; + } +} + +bitflags! { + #[derive(Deserialize, Serialize)] + #[serde(transparent)] + pub struct DutyFinderSettingsFlags : u32 { + const NONE = 0; + const UNDERSIZED_PARTY = 1 << 0; + const MINIMUM_ITEM_LEVEL = 1 << 1; + const SILENCE_ECHO = 1 << 2; + } +} + +bitflags! { + #[derive(Deserialize, Serialize)] + #[serde(transparent)] + pub struct LootRuleFlags : u32 { + const NONE = 0; + const GREED_ONLY = 1 << 0; + const LOOTMASTER = 1 << 1; + } +} + +bitflags! { + #[derive(Deserialize, Serialize)] + #[serde(transparent)] + pub struct SearchAreaFlags : u32 { + const DATA_CENTRE = 1 << 0; + const PRIVATE = 1 << 1; + const ALLIANCE_RAID = 1 << 2; + const WORLD = 1 << 3; + const ONE_PLAYER_PER_JOB = 1 << 5; + } +} + +bitflags! { + #[derive(Deserialize, Serialize)] + #[serde(transparent)] + pub struct JobFlags : u32 { + const GLADIATOR = 1 << 1; + const PUGILIST = 1 << 2; + const MARAUDER = 1 << 3; + const LANCER = 1 << 4; + const ARCHER = 1 << 5; + const CONJURER = 1 << 6; + const THAUMATURGE = 1 << 7; + const PALADIN = 1 << 8; + const MONK = 1 << 9; + const WARRIOR = 1 << 10; + const DRAGOON = 1 << 11; + const BARD = 1 << 12; + const WHITE_MAGE = 1 << 13; + const BLACK_MAGE = 1 << 14; + const ARCANIST = 1 << 15; + const SUMMONER = 1 << 16; + const SCHOLAR = 1 << 17; + const ROGUE = 1 << 18; + const NINJA = 1 << 19; + const MACHINIST = 1 << 20; + const DARK_KNIGHT = 1 << 21; + const ASTROLOGIAN = 1 << 22; + const SAMURAI = 1 << 23; + const RED_MAGE = 1 << 24; + const BLUE_MAGE = 1 << 25; + const GUNBREAKER = 1 << 26; + const DANCER = 1 << 27; + } +} + +impl JobFlags { + pub fn classjobs(&self) -> Vec { + let mut cjs = Vec::new(); + + if self.contains(Self::GLADIATOR) { + cjs.push(ClassJob::Class(Class::Gladiator)); + } + + if self.contains(Self::PUGILIST) { + cjs.push(ClassJob::Class(Class::Pugilist)); + } + + if self.contains(Self::MARAUDER) { + cjs.push(ClassJob::Class(Class::Marauder)); + } + + if self.contains(Self::LANCER) { + cjs.push(ClassJob::Class(Class::Lancer)); + } + + if self.contains(Self::ARCHER) { + cjs.push(ClassJob::Class(Class::Archer)); + } + + if self.contains(Self::CONJURER) { + cjs.push(ClassJob::Class(Class::Conjurer)); + } + + if self.contains(Self::THAUMATURGE) { + cjs.push(ClassJob::Class(Class::Thaumaturge)); + } + + if self.contains(Self::PALADIN) { + cjs.push(ClassJob::Job(Job::Paladin)); + } + + if self.contains(Self::MONK) { + cjs.push(ClassJob::Job(Job::Monk)); + } + + if self.contains(Self::WARRIOR) { + cjs.push(ClassJob::Job(Job::Warrior)); + } + + if self.contains(Self::DRAGOON) { + cjs.push(ClassJob::Job(Job::Dragoon)); + } + + if self.contains(Self::BARD) { + cjs.push(ClassJob::Job(Job::Bard)); + } + + if self.contains(Self::WHITE_MAGE) { + cjs.push(ClassJob::Job(Job::WhiteMage)); + } + + if self.contains(Self::BLACK_MAGE) { + cjs.push(ClassJob::Job(Job::BlackMage)); + } + + if self.contains(Self::ARCANIST) { + cjs.push(ClassJob::Class(Class::Arcanist)); + } + + if self.contains(Self::SUMMONER) { + cjs.push(ClassJob::Job(Job::Summoner)); + } + + if self.contains(Self::SCHOLAR) { + cjs.push(ClassJob::Job(Job::Scholar)); + } + + if self.contains(Self::ROGUE) { + cjs.push(ClassJob::Class(Class::Rogue)); + } + + if self.contains(Self::NINJA) { + cjs.push(ClassJob::Job(Job::Ninja)); + } + + if self.contains(Self::MACHINIST) { + cjs.push(ClassJob::Job(Job::Machinist)); + } + + if self.contains(Self::DARK_KNIGHT) { + cjs.push(ClassJob::Job(Job::DarkKnight)); + } + + if self.contains(Self::ASTROLOGIAN) { + cjs.push(ClassJob::Job(Job::Astrologian)); + } + + if self.contains(Self::SAMURAI) { + cjs.push(ClassJob::Job(Job::Samurai)); + } + + if self.contains(Self::RED_MAGE) { + cjs.push(ClassJob::Job(Job::RedMage)); + } + + if self.contains(Self::BLUE_MAGE) { + cjs.push(ClassJob::Job(Job::BlueMage)); + } + + if self.contains(Self::GUNBREAKER) { + cjs.push(ClassJob::Job(Job::Gunbreaker)); + } + + if self.contains(Self::DANCER) { + cjs.push(ClassJob::Job(Job::Dancer)); + } + + cjs + } +} diff --git a/src/listing_container.rs b/src/listing_container.rs new file mode 100644 index 0000000..36132f3 --- /dev/null +++ b/src/listing_container.rs @@ -0,0 +1,33 @@ +use chrono::{DateTime, Duration, Utc}; +use chrono_humanize::HumanTime; +use serde::{Deserialize, Serialize}; +use crate::listing::PartyFinderListing; + +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct ListingContainer { + #[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")] + pub updated_at: DateTime, + pub listing: PartyFinderListing, +} + +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct QueriedListing { + #[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")] + pub updated_at: DateTime, + pub time_left: f64, + pub listing: PartyFinderListing, +} + +impl QueriedListing { + pub fn human_time_left(&self) -> HumanTime { + HumanTime::from(Duration::milliseconds((self.time_left * 1000f64) as i64)) + } + + pub fn since_updated(&self) -> Duration { + Utc::now() - self.updated_at + } + + pub fn human_since_updated(&self) -> HumanTime { + HumanTime::from(-self.since_updated()) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..124f6e3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,54 @@ +#![feature(try_blocks, iter_intersperse)] + +use anyhow::Context; +use std::borrow::Cow; +use std::path::Path; +use std::sync::Arc; +use tokio::fs::File; +use tokio::io::AsyncReadExt; +use crate::config::Config; + +mod config; +mod listing; +mod listing_container; +mod base64_sestring; +mod sestring_ext; +mod web; +mod template; +mod ffxiv; + +#[cfg(test)] +mod test; + +#[tokio::main] +async fn main() { + let mut args: Vec = std::env::args().skip(1).collect(); + let config_path = if args.is_empty() { + Cow::from("./config.toml") + } else { + Cow::from(args.remove(0)) + }; + + let config = match get_config(&*config_path).await { + Ok(config) => config, + Err(e) => { + eprintln!("error: {}", e); + return; + } + }; + + if let Err(e) = self::web::start(Arc::new(config)).await { + eprintln!("error: {}", e); + eprintln!(" {:?}", e); + eprintln!("{}", e.backtrace()); + } +} + +async fn get_config>(path: P) -> anyhow::Result { + let mut f = File::open(path).await.context("could not open config file")?; + let mut toml = String::new(); + f.read_to_string(&mut toml).await.context("could not read config file")?; + let config = toml::from_str(&toml).context("could not parse config file")?; + + Ok(config) +} diff --git a/src/sestring_ext.rs b/src/sestring_ext.rs new file mode 100644 index 0000000..541484a --- /dev/null +++ b/src/sestring_ext.rs @@ -0,0 +1,21 @@ +use sestring::{Payload, SeString}; + +pub trait SeStringExt { + fn full_text(&self) -> String; +} + +impl SeStringExt for SeString { + fn full_text(&self) -> String { + self.0.iter() + .flat_map(|payload| { + match payload { + Payload::Text(t) => Some(&*t.0), + Payload::AutoTranslate(at) => crate::ffxiv::AUTO_TRANSLATE + .get(&(u32::from(at.group), at.key)) + .map(std::ops::Deref::deref), + _ => None, + } + }) + .collect() + } +} diff --git a/src/template/listings.rs b/src/template/listings.rs new file mode 100644 index 0000000..ed6b7b2 --- /dev/null +++ b/src/template/listings.rs @@ -0,0 +1,10 @@ +use askama::Template; +use crate::listing_container::QueriedListing; +use std::borrow::Borrow; +use crate::sestring_ext::SeStringExt; + +#[derive(Debug, Template)] +#[template(path = "listings.html")] +pub struct ListingsTemplate { + pub containers: Vec, +} diff --git a/src/template/mod.rs b/src/template/mod.rs new file mode 100644 index 0000000..deb0990 --- /dev/null +++ b/src/template/mod.rs @@ -0,0 +1 @@ +pub mod listings; diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..34e854e --- /dev/null +++ b/src/test.rs @@ -0,0 +1,89 @@ +use crate::listing::{PartyFinderListing, DutyType, ObjectiveFlags, ConditionFlags, DutyFinderSettingsFlags, LootRuleFlags, SearchAreaFlags, DutyCategory, PartyFinderSlot, JobFlags}; +use sestring::SeString; + +const LISTING: &str = r###" +{ + "id": 123, + "content_id_lower": 456, + "name": "VGVzdCBOYW1l", + "description": "VGhpcyBpcyBteSB0ZXN0IGRlc2NyaXB0aW9uLg==", + "created_world": 73, + "home_world": 73, + "current_world": 73, + "category": 0, + "duty": 55, + "duty_type": 2, + "beginners_welcome": false, + "seconds_remaining": 3300, + "min_item_level": 0, + "num_parties": 1, + "slots_available": 7, + "objective": 3, + "conditions": 1, + "duty_finder_settings": 0, + "loot_rules": 0, + "search_area": 1, + "slots": [ + { + "accepting": 167772160 + } + ], + "jobs_present": [ + 5, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] +}"###; + +lazy_static::lazy_static! { + static ref EXPECTED: PartyFinderListing = PartyFinderListing { + id: 123, + content_id_lower: 456, + name: SeString::parse(b"Test Name").unwrap(), + description: SeString::parse(b"This is my test description.").unwrap(), + created_world: 73, + home_world: 73, + current_world: 73, + category: DutyCategory::Duty, + duty: 55, + duty_type: DutyType::Normal, + beginners_welcome: false, + seconds_remaining: 3300, + min_item_level: 0, + num_parties: 1, + slots_available: 7, + objective: ObjectiveFlags::PRACTICE | ObjectiveFlags::DUTY_COMPLETION, + conditions: ConditionFlags::NONE, + duty_finder_settings: DutyFinderSettingsFlags::NONE, + loot_rules: LootRuleFlags::NONE, + search_area: SearchAreaFlags::DATA_CENTRE, + slots: vec![ + PartyFinderSlot { + accepting: JobFlags::DANCER | JobFlags::BLUE_MAGE, + }, + ], + jobs_present: vec![5, 0, 0, 0, 0, 0, 0, 0], + }; +} + +#[test] +fn deserialise_listing() { + let listing: PartyFinderListing = serde_json::from_str(LISTING).unwrap(); + assert_eq!( + listing, + *EXPECTED, + ) +} + +#[test] +fn serialise_listing() { + assert_eq!( + serde_json::to_string_pretty(&*EXPECTED).unwrap(), + LISTING.trim(), + ); +} diff --git a/src/web.rs b/src/web.rs new file mode 100644 index 0000000..5fae423 --- /dev/null +++ b/src/web.rs @@ -0,0 +1,231 @@ +use std::convert::{Infallible, TryFrom}; +use anyhow::{Result, Context}; +use std::sync::Arc; +use mongodb::{Client as MongoClient, Collection}; +use mongodb::options::UpdateOptions; +use mongodb::results::UpdateResult; +use tokio_stream::StreamExt; +use warp::{Filter, Reply}; +use warp::filters::BoxedFilter; +use warp::http::Uri; +use crate::config::Config; +use crate::listing::PartyFinderListing; +use crate::listing_container::{ListingContainer, QueriedListing}; +use crate::template::listings::ListingsTemplate; + +pub async fn start(config: Arc) -> Result<()> { + let state = State::new(Arc::clone(&config)).await?; + + println!("listening at {}", config.web.host); + warp::serve(router(state)) + .run(config.web.host) + .await; + Ok(()) +} + +struct State { + config: Arc, + mongo: MongoClient, +} + +impl State { + pub async fn new(config: Arc) -> Result> { + let mongo = MongoClient::with_uri_str(&config.mongo.url) + .await + .context("could not create mongodb client")?; + + Ok(Arc::new(Self { + config, + mongo, + })) + } + + pub fn collection(&self) -> Collection { + self.mongo.database("rpf").collection("listings") + } +} + +fn router(state: Arc) -> BoxedFilter<(impl Reply, )> { + index() + .or(listings(Arc::clone(&state))) + .or(contribute(Arc::clone(&state))) + .or(contribute_multiple(Arc::clone(&state))) + .or(assets()) + .boxed() +} + +fn assets() -> BoxedFilter<(impl Reply, )> { + warp::get() + .and(warp::path("assets")) + .and( + icons() + ) + .boxed() +} + +fn icons() -> BoxedFilter<(impl Reply, )> { + warp::path("icons.svg") + .and(warp::path::end()) + .and(warp::fs::file("./assets/icons.svg")) + .boxed() +} + +fn index() -> BoxedFilter<(impl Reply, )> { + let route = warp::path::end() + .map(|| warp::redirect(Uri::from_static("/listings"))); + warp::get().and(route).boxed() +} + +fn listings(state: Arc) -> BoxedFilter<(impl Reply, )> { + async fn logic(state: Arc) -> std::result::Result { + use mongodb::bson::doc; + + let res = state + .collection() + .aggregate( + [ + doc! { + "$set": { + "time_left": { + "$divide": [ + { + "$subtract": [ + { "$multiply": ["$listing.seconds_remaining", 1000] }, + { "$subtract": ["$$NOW", "$updated_at"] }, + ] + }, + 1000, + ] + }, + "updated_minute": { + "$dateTrunc": { + "date": "$updated_at", + "unit": "minute", + "binSize": 5, + }, + }, + } + }, + doc! { + "$match": { + "time_left": { + "$gte": 0, + }, + } + }, + doc! { + "$sort": { + "updated_minute": -1, + "time_left": 1, + } + } + ], + None, + ) + .await; + Ok(match res { + Ok(mut cursor) => { + let mut containers = Vec::new(); + + while let Ok(Some(container)) = cursor.try_next().await { + let res: Result = try { + let json = serde_json::to_vec(&container)?; + let result: QueriedListing = serde_json::from_slice(&json)?; + result + }; + if let Ok(listing) = res { + containers.push(listing); + } + } + + Ok(ListingsTemplate { + containers, + }) + } + Err(e) => { + eprintln!("{:#?}", e); + Ok(ListingsTemplate { + containers: Default::default(), + }) + } + }) + } + + let route = warp::path("listings") + .and(warp::path::end()) + .and_then(move || logic(Arc::clone(&state))); + + warp::get().and(route).boxed() +} + +fn contribute(state: Arc) -> BoxedFilter<(impl Reply, )> { + async fn logic(state: Arc, listing: PartyFinderListing) -> std::result::Result { + if listing.seconds_remaining > 60 * 60 { + return Ok("invalid listing".to_string()); + } + + let result = insert_listing(&*state, listing).await; + Ok(format!("{:#?}", result)) + } + + let route = warp::path("contribute") + .and(warp::path::end()) + .and(warp::body::json()) + .and_then(move |listing: PartyFinderListing| logic(Arc::clone(&state), listing)); + warp::post().and(route).boxed() +} + +fn contribute_multiple(state: Arc) -> BoxedFilter<(impl Reply, )> { + async fn logic(state: Arc, listings: Vec) -> std::result::Result { + let total = listings.len(); + let mut successful = 0; + + for listing in listings { + if listing.seconds_remaining > 60 * 60 { + continue; + } + + let result = insert_listing(&*state, listing).await; + if result.is_ok() { + successful += 1; + } + } + + Ok(format!("{}/{} updated", successful, total)) + } + + let route = warp::path("contribute") + .and(warp::path("multiple")) + .and(warp::path::end()) + .and(warp::body::json()) + .and_then(move |listings: Vec| logic(Arc::clone(&state), listings)); + warp::post().and(route).boxed() +} + +async fn insert_listing(state: &State, listing: PartyFinderListing) -> mongodb::error::Result { + use mongodb::bson::doc; + + let opts = UpdateOptions::builder() + .upsert(true) + .build(); + let value = serde_json::to_value(&listing).unwrap(); + let bson_value = mongodb::bson::Bson::try_from(value).unwrap(); + state + .collection() + .update_one( + doc! { + "listing.id": listing.id, + "listing.content_id_lower": listing.content_id_lower, + }, + doc! { + "$currentDate": { + "updated_at": true, + }, + "$set": { + "listing": bson_value, + } + }, + opts, + ) + .await +} diff --git a/templates/_frame.html b/templates/_frame.html new file mode 100644 index 0000000..f67156a --- /dev/null +++ b/templates/_frame.html @@ -0,0 +1,60 @@ + + + + + + {% block title %}{% endblock %} + + {%- block head %}{% endblock %} + +{% block body %}{% endblock %} + diff --git a/templates/listings.html b/templates/listings.html new file mode 100644 index 0000000..37bc482 --- /dev/null +++ b/templates/listings.html @@ -0,0 +1,330 @@ +{% extends "_frame.html" %} + +{% block title -%} +Remote Party Finder +{%- endblock %} + +{% block head %} + +{% endblock %} + +{% block body %} +{% for container in containers %} +{% let listing = container.listing.borrow() %} +
+
+ {% let duty_class %} + {% if listing.is_cross_world() %} + {% let duty_class = " cross" %} + {% else %} + {% let duty_class = " local" %} + {% endif %} +
{{ listing.duty_name() }}
+
+ {%- let desc = listing.description.full_text() %} + {%- if desc.is_empty() -%} + None + {%- else -%} + {{ desc }} + {%- endif -%} +
+
+ {% for slot in listing.slots() %} + {% let filled %} + {% let title %} + {% let role_class %} + {% match slot %} + {% when Ok with (slot) %} + {% let filled = " filled" %} + {% match slot.role() %} + {% when Some with (role) %} + {% let role_class = " {}"|format(role.as_str().to_lowercase()) %} + {% when None %} + {% let role_class = "".to_string() %} + {% endmatch %} + {% let title = slot.code().to_string() %} + {% when Err with (tuple) %} + {% let filled = "" %} + {% let title = tuple.1.clone() %} + {% let role_class = " {}"|format(tuple.0) %} + {% endmatch %} +
+ {% if !filled.is_empty() %} + + + + {% endif %} +
+ {% endfor %} +
{{ listing.slots_filled() }}/{{ listing.slots_available }}
+
+
+
+
+ {{ listing.name.full_text() }} @ {{ listing.home_world_string() }} + + + + + +
+
+ {{ listing.created_world_string() }} + + + + + +
+
+ {{ container.human_time_left() }} + + + + + +
+
+ {{ container.human_since_updated() }} + + + + + +
+
+
+{% endfor %} +{% endblock %}