@piyoraik/ffxiv-lodestone-character-lookup 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -40
- package/dist/data/achievements/battle_general.json +10 -0
- package/dist/data/achievements/dungeons.json +19 -0
- package/dist/data/achievements/field_ops.json +11 -0
- package/dist/data/achievements/raids.json +19 -0
- package/dist/lodestone.d.ts +14 -6
- package/dist/lodestone.d.ts.map +1 -1
- package/dist/lodestone.js +29 -19
- package/dist/lodestone.js.map +1 -1
- package/dist/lodestoneAchievements.d.ts +39 -57
- package/dist/lodestoneAchievements.d.ts.map +1 -1
- package/dist/lodestoneAchievements.js +231 -90
- package/dist/lodestoneAchievements.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ FFXIV の Lodestone を使って、以下を行うための共通ライブラリ
|
|
|
4
4
|
|
|
5
5
|
- `キャラクター名` と `サーバー名` から検索パラメータを作る
|
|
6
6
|
- キャラクター検索の先頭ヒットから「キャラクターURL(/lodestone/character/.../)」を取得する
|
|
7
|
-
-
|
|
7
|
+
- Lodestone からアチーブメントの達成状況と達成要件を取得する
|
|
8
8
|
|
|
9
9
|
## 動作環境
|
|
10
10
|
|
|
@@ -21,10 +21,13 @@ yarn add @piyoraik/ffxiv-lodestone-character-lookup
|
|
|
21
21
|
```ts
|
|
22
22
|
import {
|
|
23
23
|
buildLodestoneSearchUrl,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
fetchTopCharacterResult,
|
|
25
|
+
fetchAchievementDetailsByCategory,
|
|
26
|
+
fetchAllAchievementDetails,
|
|
27
|
+
getAllAchievementDefinitions,
|
|
28
|
+
getAchievementCategoryDefinitions,
|
|
29
|
+
getAchievementDefinitions,
|
|
30
|
+
getAchievementCategoryId
|
|
28
31
|
} from "@piyoraik/ffxiv-lodestone-character-lookup";
|
|
29
32
|
|
|
30
33
|
const creator = { name: "Hoge Fuga", world: "World" };
|
|
@@ -32,18 +35,44 @@ const creator = { name: "Hoge Fuga", world: "World" };
|
|
|
32
35
|
// 1) Lodestoneの検索URLを作る
|
|
33
36
|
const searchUrl = buildLodestoneSearchUrl(creator);
|
|
34
37
|
|
|
35
|
-
// 2)
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
console.log(
|
|
38
|
+
// 2) 検索結果の状態を取得する
|
|
39
|
+
const searchResult = await fetchTopCharacterResult(searchUrl);
|
|
40
|
+
if (searchResult.status !== "ok") {
|
|
41
|
+
throw new Error(`キャラクターが取得できません: ${searchResult.status}`);
|
|
42
|
+
}
|
|
43
|
+
const characterUrl = searchResult.characterUrl;
|
|
44
|
+
|
|
45
|
+
// 3) カテゴリ指定で取得する
|
|
46
|
+
const raids = await fetchAchievementDetailsByCategory(characterUrl, "raids");
|
|
47
|
+
console.log(raids);
|
|
48
|
+
const fieldOps = await fetchAchievementDetailsByCategory(characterUrl, "field_ops");
|
|
49
|
+
console.log(fieldOps);
|
|
50
|
+
const dungeons = await fetchAchievementDetailsByCategory(characterUrl, "dungeons");
|
|
51
|
+
console.log(dungeons);
|
|
52
|
+
|
|
53
|
+
// 4) 全カテゴリをまとめて取得する
|
|
54
|
+
const all = await fetchAllAchievementDetails(characterUrl);
|
|
55
|
+
console.log(all);
|
|
56
|
+
|
|
57
|
+
// 定義をまとめて取得する場合
|
|
58
|
+
const categories = getAchievementCategoryDefinitions();
|
|
59
|
+
const allAchievements = getAllAchievementDefinitions();
|
|
60
|
+
const raidsDefinitions = getAchievementDefinitions("raids");
|
|
61
|
+
const raidsCategoryId = getAchievementCategoryId("raids");
|
|
62
|
+
console.log(categories.length, allAchievements.length, raidsDefinitions.length, raidsCategoryId);
|
|
63
|
+
|
|
64
|
+
/*
|
|
65
|
+
{
|
|
66
|
+
status: "ok",
|
|
67
|
+
lodestone: "https://jp.finalfantasyxiv.com/lodestone/character/12345/",
|
|
68
|
+
achievements: {
|
|
69
|
+
raids: [
|
|
70
|
+
{ name: "絶バハムートを狩りし者", date: "2014/06/10", requirement: "絶バハムート討滅戦で、バハムート・プライムを討伐する" },
|
|
71
|
+
{ name: "万魔殿の辺獄を完全制覇せし者:ランク1", date: null, requirement: "万魔殿パンデモニウム零式:辺獄編を攻略する" }
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
*/
|
|
47
76
|
```
|
|
48
77
|
|
|
49
78
|
## 関数一覧
|
|
@@ -51,35 +80,78 @@ console.log(result);
|
|
|
51
80
|
| 関数 | 返り値 | 用途 |
|
|
52
81
|
|---|---|---|
|
|
53
82
|
| `buildLodestoneSearchUrl(info)` | `string` | キャラクター検索URLを生成 |
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
83
|
+
| `fetchTopCharacterResult(searchUrl)` | `Promise<{ status, characterUrl?, reason? }>` | 検索結果の状態を取得(非公開は `private`) |
|
|
84
|
+
| `fetchAchievementDetailsByCategory(characterUrl, category)` | `Promise<{ status, lodestone, achievements, reason? }>` | カテゴリ指定で達成状況/達成要件を取得 |
|
|
85
|
+
| `fetchAllAchievementDetails(characterUrl)` | `Promise<{ status, lodestone, achievements, reason? }>` | 全カテゴリの達成状況/達成要件を取得 |
|
|
86
|
+
| `getAchievementDefinitions(category)` | `AchievementDefinition[]` | カテゴリ指定で定義を取得 |
|
|
87
|
+
| `getAllAchievementDefinitions()` | `AchievementDefinition[]` | 全カテゴリの定義を取得 |
|
|
88
|
+
| `getAchievementCategoryDefinitions()` | `AchievementCategoryDefinition[]` | カテゴリ一覧(ID/定義)を取得 |
|
|
89
|
+
| `getAchievementCategoryId(category)` | `number \| undefined` | カテゴリ名からカテゴリIDを取得 |
|
|
61
90
|
|
|
62
|
-
##
|
|
91
|
+
## 対応カテゴリ一覧
|
|
92
|
+
|
|
93
|
+
| category | 説明 | categoryId |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `raids` | レイド(高難度) | `4` |
|
|
96
|
+
| `field_ops` | 特殊フィールド探索 | `71` |
|
|
97
|
+
| `dungeons` | ダンジョン | `2` |
|
|
98
|
+
| `battle_general` | バトル(全般) | `1` |
|
|
99
|
+
|
|
100
|
+
## 対応アチーブメント(カテゴリ別)
|
|
63
101
|
|
|
64
102
|
このライブラリが達成判定に対応しているアチーブメントの一覧です。
|
|
65
103
|
アップデートごとにベストエフォートで更新し、必要であればIssueやプルリクを頂ければ優先的に対応します。
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
|
70
|
-
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
104
|
+
達成要件の更新には `npm run update-readme-requirements` を使用してください。
|
|
105
|
+
|
|
106
|
+
### レイド(高難度)
|
|
107
|
+
| 正式名 | 達成要件 |
|
|
108
|
+
|---|---|
|
|
109
|
+
| 絶バハムートを狩りし者 | 絶バハムート討滅戦で、バハムート・プライムを討伐する |
|
|
110
|
+
| 絶アルテマウェポンを破壊せし者 | 絶アルテマウェポン破壊作戦で、アルテマウェポンを討伐する |
|
|
111
|
+
| 絶アレキサンダーを破壊せし者 | 絶アレキサンダー討滅戦で、パーフェクト・アレキサンダーを討伐する |
|
|
112
|
+
| 絶竜詩戦争を平定せし者 | 絶竜詩戦争を平定する |
|
|
113
|
+
| 絶オメガ検証戦を完遂せし者 | オメガの求める検証を完遂する |
|
|
114
|
+
| 絶もうひとつの未来を見届けし者 | 光と闇の巫女の運命を集約させる |
|
|
115
|
+
| 万魔殿の辺獄を完全制覇せし者:ランク1 | 万魔殿パンデモニウム零式:辺獄編を攻略する |
|
|
116
|
+
| 万魔殿の煉獄を完全制覇せし者:ランク1 | 万魔殿パンデモニウム零式:煉獄編を攻略する |
|
|
117
|
+
| 万魔殿の天獄を完全制覇せし者:ランク1 | 万魔殿パンデモニウム零式:天獄編を攻略する |
|
|
118
|
+
| アルカディアのライトヘビー級を完全制覇せし者:ランク1 | 至天の座アルカディア零式:ライトヘビー級を攻略する |
|
|
119
|
+
| アルカディアのクルーザー級を完全制覇せし者:ランク1 | 至天の座アルカディア零式:クルーザー級を攻略する |
|
|
120
|
+
| アルカディアのヘビー級を完全制覇せし者:ランク1 | 至天の座アルカディア零式:ヘビー級を攻略する |
|
|
121
|
+
|
|
122
|
+
### 特殊フィールド探索
|
|
123
|
+
| 正式名 | 達成要件 |
|
|
124
|
+
|---|---|
|
|
125
|
+
| バルデシオンアーセナルの覇者:ランク1 | バルデシオンアーセナルを初めて攻略する |
|
|
126
|
+
| ボズヤの彗星 | 堅守彗星章、勇猛彗星章、救命彗星章を、各10個入手する |
|
|
127
|
+
| グンヒルド・ディルーブラムを完全制覇せし者:ランク1 | グンヒルド・ディルーブラム零式を攻略する |
|
|
128
|
+
| 力の塔を制覇せし者:ランク1 | 蜃気楼の島 クレセントアイルにて、フォークタワー:力の塔を初めて攻略する |
|
|
129
|
+
|
|
130
|
+
### ダンジョン
|
|
131
|
+
| 正式名 | 達成要件 |
|
|
132
|
+
|---|---|
|
|
133
|
+
| 語り継がれし冒険譚:ランク3 | ダンジョン、討伐・討滅戦を計10,000回攻略する |
|
|
134
|
+
| 孤独なる挑戦者:ランク3 | 死者の宮殿にソロでB1から突入しB200まで攻略する |
|
|
135
|
+
| 埋もれた財宝:ランク7 | 埋もれた財宝を30,000個発見した |
|
|
136
|
+
| ここ掘れワンワン:ランク2 | 埋もれた財宝を、魔土器:財宝感知もしくは魔科学器:財宝感知を使わずに100個発見した |
|
|
137
|
+
| 孤高なる挑戦者:ランク2 | アメノミハシラにソロで1層から突入し100層まで攻略する |
|
|
138
|
+
| 崇高なる挑戦者:ランク2 | オルト・エウレカにソロでB1から突入しB100まで攻略する |
|
|
139
|
+
| 至高なる挑戦者:ランク2 | ピルグリム・トラバースにソロで第1巡礼路から突入し第100巡礼路まで攻略する |
|
|
140
|
+
| 死せる巡礼路の果てへ | 詩想エミネントグリーフ討滅戦を供物を最大限捧げた状態で攻略する |
|
|
141
|
+
| 異聞シラディハ水道を完全制覇せし者 | 異聞シラディハ水道 零式(アナザーダンジョン)を攻略する |
|
|
142
|
+
| 異聞六根山を完全制覇せし者 | 異聞六根山 零式(アナザーダンジョン)を攻略する |
|
|
143
|
+
| 異聞アロアロ島を完全制覇せし者 | 異聞アロアロ島 零式(アナザーダンジョン)を攻略する |
|
|
144
|
+
| 異聞奇譚の勇傑 | アチーブメント「異聞シラディハ水道を完全制覇せし者」「異聞六根山を完全制覇せし者」「異聞アロアロ島を完全制覇せし者」をすべて達成する |
|
|
145
|
+
|
|
146
|
+
### バトル(全般)
|
|
147
|
+
| 正式名 | 達成要件 |
|
|
148
|
+
|---|---|
|
|
149
|
+
| 豪傑の青魔道士 | アチーブメント「大迷宮を極めし青魔道士」と「機工城を極めし青魔道士」を達成する |
|
|
150
|
+
| 機工城を極めし青魔道士 | アチーブメント「起動せし機工城を制覇せし青魔道士」「律動せし機工城を制覇せし青魔道士」「天動せし機工城を制覇せし青魔道士」をすべて達成する |
|
|
151
|
+
| 大迷宮を極めし青魔道士 | アチーブメント「始まりの大迷宮を制覇せし青魔道士」「第二の大迷宮を制覇せし青魔道士」「終わりの大迷宮を制覇せし青魔道士」をすべて達成する |
|
|
81
152
|
|
|
82
153
|
## 注意
|
|
83
154
|
|
|
84
155
|
- Lodestone検索は「先頭ヒット」を採用します(同名が複数いる場合、意図と違うキャラに当たる可能性があります)
|
|
85
156
|
- HTML構造(class名など)が変わるとパースが壊れます(その場合はバージョン更新で追随します)
|
|
157
|
+
- アチーブメントの達成要件は Lodestone から引用しています
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"category": "dungeons",
|
|
4
|
+
"categoryId": 2,
|
|
5
|
+
"achievements": [
|
|
6
|
+
{ "name": "語り継がれし冒険譚:ランク3" },
|
|
7
|
+
{ "name": "孤独なる挑戦者:ランク3" },
|
|
8
|
+
{ "name": "埋もれた財宝:ランク7" },
|
|
9
|
+
{ "name": "ここ掘れワンワン:ランク2" },
|
|
10
|
+
{ "name": "孤高なる挑戦者:ランク2" },
|
|
11
|
+
{ "name": "崇高なる挑戦者:ランク2" },
|
|
12
|
+
{ "name": "至高なる挑戦者:ランク2" },
|
|
13
|
+
{ "name": "死せる巡礼路の果てへ" },
|
|
14
|
+
{ "name": "異聞シラディハ水道を完全制覇せし者" },
|
|
15
|
+
{ "name": "異聞六根山を完全制覇せし者" },
|
|
16
|
+
{ "name": "異聞アロアロ島を完全制覇せし者" },
|
|
17
|
+
{ "name": "異聞奇譚の勇傑" }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"category": "raids",
|
|
4
|
+
"categoryId": 4,
|
|
5
|
+
"achievements": [
|
|
6
|
+
{ "name": "絶バハムートを狩りし者" },
|
|
7
|
+
{ "name": "絶アルテマウェポンを破壊せし者" },
|
|
8
|
+
{ "name": "絶アレキサンダーを破壊せし者" },
|
|
9
|
+
{ "name": "絶竜詩戦争を平定せし者" },
|
|
10
|
+
{ "name": "絶オメガ検証戦を完遂せし者" },
|
|
11
|
+
{ "name": "絶もうひとつの未来を見届けし者" },
|
|
12
|
+
{ "name": "万魔殿の辺獄を完全制覇せし者:ランク1" },
|
|
13
|
+
{ "name": "万魔殿の煉獄を完全制覇せし者:ランク1" },
|
|
14
|
+
{ "name": "万魔殿の天獄を完全制覇せし者:ランク1" },
|
|
15
|
+
{ "name": "アルカディアのライトヘビー級を完全制覇せし者:ランク1" },
|
|
16
|
+
{ "name": "アルカディアのクルーザー級を完全制覇せし者:ランク1" },
|
|
17
|
+
{ "name": "アルカディアのヘビー級を完全制覇せし者:ランク1" }
|
|
18
|
+
]
|
|
19
|
+
}
|
package/dist/lodestone.d.ts
CHANGED
|
@@ -2,6 +2,18 @@ export type CreatorInfo = {
|
|
|
2
2
|
name: string;
|
|
3
3
|
world: string;
|
|
4
4
|
};
|
|
5
|
+
export type CharacterSearchStatus = "ok" | "private" | "ng";
|
|
6
|
+
export type CharacterSearchReason = "search_hidden" | "empty_html" | "structure_mismatch" | "http_error";
|
|
7
|
+
export type CharacterSearchResult = {
|
|
8
|
+
status: "ok";
|
|
9
|
+
characterUrl: string;
|
|
10
|
+
} | {
|
|
11
|
+
status: "private";
|
|
12
|
+
reason: CharacterSearchReason;
|
|
13
|
+
} | {
|
|
14
|
+
status: "ng";
|
|
15
|
+
reason: CharacterSearchReason;
|
|
16
|
+
};
|
|
5
17
|
/**
|
|
6
18
|
* Lodestone のキャラクター検索URLを生成します。
|
|
7
19
|
* 例:
|
|
@@ -9,11 +21,7 @@ export type CreatorInfo = {
|
|
|
9
21
|
*/
|
|
10
22
|
export declare function buildLodestoneSearchUrl(info: CreatorInfo): string;
|
|
11
23
|
/**
|
|
12
|
-
*
|
|
13
|
-
*/
|
|
14
|
-
export declare function parseTopCharacterUrlFromSearchHtml(html: string): string | undefined;
|
|
15
|
-
/**
|
|
16
|
-
* Lodestone のキャラクター検索を行い、先頭にヒットしたキャラクターURLを返します。
|
|
24
|
+
* Lodestone のキャラクター検索を行い、検索結果の状態を返します。
|
|
17
25
|
*/
|
|
18
|
-
export declare function
|
|
26
|
+
export declare function fetchTopCharacterResult(searchUrl: string): Promise<CharacterSearchResult>;
|
|
19
27
|
//# sourceMappingURL=lodestone.d.ts.map
|
package/dist/lodestone.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lodestone.d.ts","sourceRoot":"","sources":["../src/lodestone.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"lodestone.d.ts","sourceRoot":"","sources":["../src/lodestone.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;AAE5D,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG,YAAY,GAAG,oBAAoB,GAAG,YAAY,CAAC;AAEzG,MAAM,MAAM,qBAAqB,GAC7B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GACpD;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,CAAC;AAEpD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAsBjE;AAoBD;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAgB/F"}
|
package/dist/lodestone.js
CHANGED
|
@@ -37,8 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.buildLodestoneSearchUrl = buildLodestoneSearchUrl;
|
|
40
|
-
exports.
|
|
41
|
-
exports.fetchTopCharacterUrl = fetchTopCharacterUrl;
|
|
40
|
+
exports.fetchTopCharacterResult = fetchTopCharacterResult;
|
|
42
41
|
const axios_1 = __importDefault(require("axios"));
|
|
43
42
|
const cheerio = __importStar(require("cheerio"));
|
|
44
43
|
const LODESTONE_BASE_URL = "https://jp.finalfantasyxiv.com";
|
|
@@ -69,26 +68,37 @@ function buildLodestoneSearchUrl(info) {
|
|
|
69
68
|
/**
|
|
70
69
|
* キャラクター検索結果HTMLから、先頭に表示されるキャラクターURLを取得します。
|
|
71
70
|
*/
|
|
72
|
-
function
|
|
73
|
-
const
|
|
71
|
+
function parseTopCharacterResultFromSearchHtml(html) {
|
|
72
|
+
const raw = html.trim();
|
|
73
|
+
if (!raw)
|
|
74
|
+
return { status: "ng", reason: "empty_html" };
|
|
75
|
+
const $ = cheerio.load(raw);
|
|
74
76
|
const href = $("a.entry__link").first().attr("href");
|
|
75
|
-
if (
|
|
76
|
-
return
|
|
77
|
-
|
|
77
|
+
if (href)
|
|
78
|
+
return { status: "ok", characterUrl: new URL(href, LODESTONE_BASE_URL).toString() };
|
|
79
|
+
if ($("li.entry").length === 0) {
|
|
80
|
+
return { status: "private", reason: "search_hidden" };
|
|
81
|
+
}
|
|
82
|
+
return { status: "ng", reason: "structure_mismatch" };
|
|
78
83
|
}
|
|
79
84
|
/**
|
|
80
|
-
* Lodestone
|
|
85
|
+
* Lodestone のキャラクター検索を行い、検索結果の状態を返します。
|
|
81
86
|
*/
|
|
82
|
-
async function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
async function fetchTopCharacterResult(searchUrl) {
|
|
88
|
+
try {
|
|
89
|
+
const response = await axios_1.default.get(searchUrl, {
|
|
90
|
+
responseType: "text",
|
|
91
|
+
headers: {
|
|
92
|
+
Accept: "text/html,application/xhtml+xml",
|
|
93
|
+
"Accept-Language": "ja,en;q=0.8",
|
|
94
|
+
"User-Agent": "ffxiv-lodestone-character-lookup/0.1 (+https://jp.finalfantasyxiv.com)"
|
|
95
|
+
},
|
|
96
|
+
timeout: 30_000
|
|
97
|
+
});
|
|
98
|
+
return parseTopCharacterResultFromSearchHtml(response.data);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return { status: "ng", reason: "http_error" };
|
|
102
|
+
}
|
|
93
103
|
}
|
|
94
104
|
//# sourceMappingURL=lodestone.js.map
|
package/dist/lodestone.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lodestone.js","sourceRoot":"","sources":["../src/lodestone.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"lodestone.js","sourceRoot":"","sources":["../src/lodestone.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,76 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* アチーブメントの正式名定義(SSOT)。
|
|
3
3
|
*
|
|
4
|
-
* 定義は `
|
|
4
|
+
* 定義は `data/achievements/*.json` を同梱して読み取ります。
|
|
5
5
|
*/
|
|
6
|
-
export type
|
|
7
|
-
export type HighEndAchievementDefinition = {
|
|
6
|
+
export type AchievementDefinition = {
|
|
8
7
|
name: string;
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
};
|
|
9
|
+
export type AchievementCategoryDefinition = {
|
|
10
|
+
version: number;
|
|
11
|
+
category: string;
|
|
12
|
+
categoryId: number;
|
|
13
|
+
achievements: AchievementDefinition[];
|
|
11
14
|
};
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
export declare function getHighEndAchievements(): HighEndAchievementDefinition[];
|
|
16
|
-
export type HighEndAchievementName = string;
|
|
17
|
-
/**
|
|
18
|
-
* 互換用: 旧型名(`UltimateAchievementName`)。
|
|
19
|
-
*/
|
|
20
|
-
export type UltimateAchievementName = HighEndAchievementName;
|
|
21
|
-
/**
|
|
22
|
-
* 互換用: 旧API名(`getUltimateAchievements`)。
|
|
23
|
-
*/
|
|
24
|
-
export declare function getUltimateAchievements(): HighEndAchievementDefinition[];
|
|
25
|
-
/**
|
|
26
|
-
* 正式名 → 略称のルックアップを返します。
|
|
27
|
-
*/
|
|
28
|
-
export declare function getHighEndAchievementShortMap(): Map<HighEndAchievementName, string>;
|
|
29
|
-
/**
|
|
30
|
-
* 互換用: 旧API名(`getUltimateAchievementShortMap`)。
|
|
31
|
-
*/
|
|
32
|
-
export declare function getUltimateAchievementShortMap(): Map<HighEndAchievementName, string>;
|
|
33
|
-
/**
|
|
34
|
-
* 正式名 → 種別(絶/零式)のルックアップを返します。
|
|
35
|
-
*/
|
|
36
|
-
export declare function getHighEndAchievementGroupMap(): Map<HighEndAchievementName, HighEndAchievementGroup>;
|
|
37
|
-
/**
|
|
38
|
-
* 互換用: 旧API名(`getUltimateAchievementGroupMap`)。
|
|
16
|
+
* カテゴリ指定でアチーブ定義を取得します。
|
|
39
17
|
*/
|
|
40
|
-
export declare function
|
|
18
|
+
export declare function getAchievementDefinitions(category: string): AchievementDefinition[];
|
|
41
19
|
/**
|
|
42
|
-
*
|
|
20
|
+
* 全カテゴリのアチーブ定義を取得します。
|
|
43
21
|
*/
|
|
44
|
-
export declare function
|
|
22
|
+
export declare function getAllAchievementDefinitions(): AchievementDefinition[];
|
|
45
23
|
/**
|
|
46
|
-
*
|
|
24
|
+
* カテゴリ定義一覧を取得します。
|
|
47
25
|
*/
|
|
48
|
-
export declare function
|
|
26
|
+
export declare function getAchievementCategoryDefinitions(): AchievementCategoryDefinition[];
|
|
49
27
|
/**
|
|
50
|
-
*
|
|
28
|
+
* カテゴリ名から、LodestoneのカテゴリIDを取得します。
|
|
51
29
|
*/
|
|
52
|
-
export declare function
|
|
53
|
-
export type
|
|
54
|
-
|
|
55
|
-
|
|
30
|
+
export declare function getAchievementCategoryId(category: string): number | undefined;
|
|
31
|
+
export type HighEndAchievementStatus = "ok" | "private" | "ng";
|
|
32
|
+
export type HighEndAchievementStatusReason = "achievements_private" | "unknown_category" | "http_403" | "http_error" | "empty_html" | "structure_mismatch" | "invalid_character_url";
|
|
33
|
+
export type AchievementEntry = {
|
|
34
|
+
name: string;
|
|
35
|
+
date: string | null;
|
|
36
|
+
requirement?: string | null;
|
|
37
|
+
};
|
|
38
|
+
export type HighEndAchievementDetailResult = {
|
|
39
|
+
status: HighEndAchievementStatus;
|
|
40
|
+
reason?: HighEndAchievementStatusReason;
|
|
41
|
+
lodestone: string;
|
|
42
|
+
achievements: Record<string, AchievementEntry[]>;
|
|
43
|
+
};
|
|
44
|
+
export type AllAchievementDetailResult = {
|
|
45
|
+
status: HighEndAchievementStatus;
|
|
46
|
+
reason?: HighEndAchievementStatusReason;
|
|
47
|
+
lodestone: string;
|
|
48
|
+
achievements: Record<string, AchievementEntry[]>;
|
|
56
49
|
};
|
|
57
50
|
/**
|
|
58
|
-
*
|
|
59
|
-
*/
|
|
60
|
-
export type UltimateAchievementParseResult = HighEndAchievementParseResult;
|
|
61
|
-
/**
|
|
62
|
-
* アチーブメント一覧HTMLから、指定した高難度(絶/零式)アチーブの達成状況を判定します。
|
|
63
|
-
*
|
|
64
|
-
* 判定方法:
|
|
65
|
-
* - 対象の `<li class="entry">` 内に `time.entry__activity__time` が存在するか(=日付が入る)
|
|
66
|
-
*/
|
|
67
|
-
export declare function parseHighEndClearsFromAchievementHtml(html: string): HighEndAchievementParseResult;
|
|
68
|
-
/**
|
|
69
|
-
* 互換用: 旧API名(`parseUltimateClearsFromAchievementHtml`)。
|
|
51
|
+
* キャラクターURLとカテゴリを指定して、アチーブを取得して返します。
|
|
70
52
|
*/
|
|
71
|
-
export declare function
|
|
53
|
+
export declare function fetchAchievementDetailsByCategory(characterUrl: string, category: string): Promise<HighEndAchievementDetailResult>;
|
|
72
54
|
/**
|
|
73
|
-
*
|
|
55
|
+
* 全カテゴリのアチーブを取得して返します。
|
|
74
56
|
*/
|
|
75
|
-
export declare function
|
|
57
|
+
export declare function fetchAllAchievementDetails(characterUrl: string): Promise<AllAchievementDetailResult>;
|
|
76
58
|
//# sourceMappingURL=lodestoneAchievements.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lodestoneAchievements.d.ts","sourceRoot":"","sources":["../src/lodestoneAchievements.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lodestoneAchievements.d.ts","sourceRoot":"","sources":["../src/lodestoneAchievements.ts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,qBAAqB,EAAE,CAAC;CACvC,CAAC;AAyBF;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAEnF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,IAAI,qBAAqB,EAAE,CAMtE;AAED;;GAEG;AACH,wBAAgB,iCAAiC,IAAI,6BAA6B,EAAE,CAEnF;AAkBD;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE7E;AAgBD,MAAM,MAAM,wBAAwB,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;AAE/D,MAAM,MAAM,8BAA8B,GACtC,sBAAsB,GACtB,kBAAkB,GAClB,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,oBAAoB,GACpB,uBAAuB,CAAC;AAE5B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,CAAC,EAAE,8BAA8B,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,CAAC,EAAE,8BAA8B,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;CAClD,CAAC;AAmHF;;GAEG;AACH,wBAAsB,iCAAiC,CACrD,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,8BAA8B,CAAC,CAkEzC;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,0BAA0B,CAAC,CAkBrC"}
|
|
@@ -36,90 +36,69 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.
|
|
40
|
-
exports.
|
|
41
|
-
exports.
|
|
42
|
-
exports.
|
|
43
|
-
exports.
|
|
44
|
-
exports.
|
|
45
|
-
exports.parseCharacterIdFromUrl = parseCharacterIdFromUrl;
|
|
46
|
-
exports.buildHighEndAchievementCategoryUrl = buildHighEndAchievementCategoryUrl;
|
|
47
|
-
exports.buildAchievementCategoryUrl = buildAchievementCategoryUrl;
|
|
48
|
-
exports.parseHighEndClearsFromAchievementHtml = parseHighEndClearsFromAchievementHtml;
|
|
49
|
-
exports.parseUltimateClearsFromAchievementHtml = parseUltimateClearsFromAchievementHtml;
|
|
50
|
-
exports.fetchAchievementCategoryHtml = fetchAchievementCategoryHtml;
|
|
39
|
+
exports.getAchievementDefinitions = getAchievementDefinitions;
|
|
40
|
+
exports.getAllAchievementDefinitions = getAllAchievementDefinitions;
|
|
41
|
+
exports.getAchievementCategoryDefinitions = getAchievementCategoryDefinitions;
|
|
42
|
+
exports.getAchievementCategoryId = getAchievementCategoryId;
|
|
43
|
+
exports.fetchAchievementDetailsByCategory = fetchAchievementDetailsByCategory;
|
|
44
|
+
exports.fetchAllAchievementDetails = fetchAllAchievementDetails;
|
|
51
45
|
const axios_1 = __importDefault(require("axios"));
|
|
52
46
|
const cheerio = __importStar(require("cheerio"));
|
|
53
47
|
const node_fs_1 = require("node:fs");
|
|
54
48
|
const node_path_1 = require("node:path");
|
|
55
49
|
const LODESTONE_BASE_URL = "https://jp.finalfantasyxiv.com";
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (cachedDefinitions)
|
|
64
|
-
return cachedDefinitions;
|
|
65
|
-
const rawText = (0, node_fs_1.readFileSync)(DEFINITIONS_FILE, "utf8");
|
|
66
|
-
const raw = JSON.parse(rawText);
|
|
67
|
-
cachedDefinitions = raw.achievements ?? [];
|
|
68
|
-
return cachedDefinitions;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* 高難度アチーブ定義を取得します。
|
|
72
|
-
*/
|
|
73
|
-
function getHighEndAchievements() {
|
|
74
|
-
return loadDefinitions();
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* 互換用: 旧API名(`getUltimateAchievements`)。
|
|
78
|
-
*/
|
|
79
|
-
function getUltimateAchievements() {
|
|
80
|
-
return getHighEndAchievements();
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* 正式名 → 略称のルックアップを返します。
|
|
84
|
-
*/
|
|
85
|
-
function getHighEndAchievementShortMap() {
|
|
86
|
-
if (cachedShortMap)
|
|
87
|
-
return cachedShortMap;
|
|
50
|
+
const DEFINITIONS_DIR = (0, node_path_1.resolve)(__dirname, "data", "achievements");
|
|
51
|
+
let cachedCategoryDefinitions;
|
|
52
|
+
const cachedNameSets = new Map();
|
|
53
|
+
function loadCategoryDefinitions() {
|
|
54
|
+
if (cachedCategoryDefinitions)
|
|
55
|
+
return cachedCategoryDefinitions;
|
|
56
|
+
const files = (0, node_fs_1.readdirSync)(DEFINITIONS_DIR).filter((file) => file.endsWith(".json"));
|
|
88
57
|
const map = new Map();
|
|
89
|
-
for (const
|
|
90
|
-
|
|
91
|
-
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
const rawText = (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(DEFINITIONS_DIR, file), "utf8");
|
|
60
|
+
const raw = JSON.parse(rawText);
|
|
61
|
+
if (!raw.category || !Array.isArray(raw.achievements))
|
|
62
|
+
continue;
|
|
63
|
+
map.set(raw.category, raw);
|
|
64
|
+
}
|
|
65
|
+
cachedCategoryDefinitions = map;
|
|
92
66
|
return map;
|
|
93
67
|
}
|
|
68
|
+
function loadDefinitions(category) {
|
|
69
|
+
const raw = loadCategoryDefinitions().get(category);
|
|
70
|
+
const definitions = raw?.achievements ?? [];
|
|
71
|
+
return definitions;
|
|
72
|
+
}
|
|
94
73
|
/**
|
|
95
|
-
*
|
|
74
|
+
* カテゴリ指定でアチーブ定義を取得します。
|
|
96
75
|
*/
|
|
97
|
-
function
|
|
98
|
-
return
|
|
76
|
+
function getAchievementDefinitions(category) {
|
|
77
|
+
return loadDefinitions(category);
|
|
99
78
|
}
|
|
100
79
|
/**
|
|
101
|
-
*
|
|
80
|
+
* 全カテゴリのアチーブ定義を取得します。
|
|
102
81
|
*/
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
cachedGroupMap = map;
|
|
110
|
-
return map;
|
|
82
|
+
function getAllAchievementDefinitions() {
|
|
83
|
+
const all = [];
|
|
84
|
+
for (const category of loadCategoryDefinitions().keys()) {
|
|
85
|
+
all.push(...loadDefinitions(category));
|
|
86
|
+
}
|
|
87
|
+
return all;
|
|
111
88
|
}
|
|
112
89
|
/**
|
|
113
|
-
*
|
|
90
|
+
* カテゴリ定義一覧を取得します。
|
|
114
91
|
*/
|
|
115
|
-
function
|
|
116
|
-
return
|
|
92
|
+
function getAchievementCategoryDefinitions() {
|
|
93
|
+
return Array.from(loadCategoryDefinitions().values());
|
|
117
94
|
}
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
95
|
+
function getAchievementNameSet(category) {
|
|
96
|
+
const cached = cachedNameSets.get(category);
|
|
97
|
+
if (cached)
|
|
98
|
+
return cached;
|
|
99
|
+
const nameSet = new Set(loadDefinitions(category).map((a) => a.name));
|
|
100
|
+
cachedNameSets.set(category, nameSet);
|
|
101
|
+
return nameSet;
|
|
123
102
|
}
|
|
124
103
|
/**
|
|
125
104
|
* キャラクターURL(例: `https://.../lodestone/character/12345/`)から characterId を抽出します。
|
|
@@ -129,51 +108,213 @@ function parseCharacterIdFromUrl(characterUrl) {
|
|
|
129
108
|
return match?.[1];
|
|
130
109
|
}
|
|
131
110
|
/**
|
|
132
|
-
* Lodestone
|
|
111
|
+
* カテゴリ名から、LodestoneのカテゴリIDを取得します。
|
|
133
112
|
*/
|
|
134
|
-
function
|
|
135
|
-
|
|
136
|
-
if (!characterId)
|
|
137
|
-
return undefined;
|
|
138
|
-
return new URL(`/lodestone/character/${characterId}/achievement/category/${HIGH_END_CATEGORY_ID}/#anchor_achievement`, LODESTONE_BASE_URL).toString();
|
|
113
|
+
function getAchievementCategoryId(category) {
|
|
114
|
+
return loadCategoryDefinitions().get(category)?.categoryId;
|
|
139
115
|
}
|
|
140
116
|
/**
|
|
141
|
-
*
|
|
117
|
+
* カテゴリ名を指定して、Lodestone のアチーブメント一覧URLを生成します。
|
|
142
118
|
*/
|
|
143
|
-
function
|
|
144
|
-
|
|
119
|
+
function buildAchievementCategoryUrlByCategory(characterUrl, category) {
|
|
120
|
+
const categoryId = getAchievementCategoryId(category);
|
|
121
|
+
if (!categoryId)
|
|
122
|
+
return undefined;
|
|
123
|
+
const characterId = parseCharacterIdFromUrl(characterUrl);
|
|
124
|
+
if (!characterId)
|
|
125
|
+
return undefined;
|
|
126
|
+
return new URL(`/lodestone/character/${characterId}/achievement/category/${categoryId}/#anchor_achievement`, LODESTONE_BASE_URL).toString();
|
|
145
127
|
}
|
|
146
128
|
/**
|
|
147
|
-
* アチーブメント一覧HTML
|
|
129
|
+
* アチーブメント一覧HTMLから、指定カテゴリのアチーブ達成状況を抽出します。
|
|
148
130
|
*
|
|
149
|
-
*
|
|
150
|
-
* -
|
|
131
|
+
* - `date` が取得できない場合は `null`
|
|
132
|
+
* - 対象アチーブが1件も見つからない場合は `private`
|
|
151
133
|
*/
|
|
152
|
-
function
|
|
134
|
+
function parseAchievementDetailsFromHtml(html, characterUrl, category) {
|
|
153
135
|
const $ = cheerio.load(html);
|
|
154
|
-
const targetSet =
|
|
155
|
-
const
|
|
136
|
+
const targetSet = getAchievementNameSet(category);
|
|
137
|
+
const entries = [];
|
|
156
138
|
let foundAny = false;
|
|
139
|
+
const totalEntries = $("li.entry").length;
|
|
140
|
+
const bodyText = $("body").text();
|
|
141
|
+
const isPrivate = bodyText.includes("非公開") || bodyText.includes("プライバシー") || bodyText.includes("公開されていません");
|
|
142
|
+
if (!html.trim()) {
|
|
143
|
+
return {
|
|
144
|
+
status: "ng",
|
|
145
|
+
reason: "empty_html",
|
|
146
|
+
lodestone: characterUrl,
|
|
147
|
+
achievements: { [category]: [] }
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const parseDate = (raw) => {
|
|
151
|
+
const text = raw.trim();
|
|
152
|
+
if (!text)
|
|
153
|
+
return null;
|
|
154
|
+
const match = text.match(/ldst_strftime\((\d+),\s*'YMD'\)/);
|
|
155
|
+
if (!match)
|
|
156
|
+
return text;
|
|
157
|
+
const seconds = Number(match[1]);
|
|
158
|
+
if (!Number.isFinite(seconds))
|
|
159
|
+
return null;
|
|
160
|
+
const date = new Date(seconds * 1000);
|
|
161
|
+
const y = date.getFullYear();
|
|
162
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
163
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
164
|
+
return `${y}/${m}/${d}`;
|
|
165
|
+
};
|
|
157
166
|
$("li.entry").each((_, el) => {
|
|
158
167
|
const entry = $(el);
|
|
159
168
|
const name = entry.find("p.entry__activity__txt").first().text().trim();
|
|
160
169
|
if (!targetSet.has(name))
|
|
161
170
|
return;
|
|
162
171
|
foundAny = true;
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
172
|
+
const rawDateText = entry.find("time.entry__activity__time").first().text();
|
|
173
|
+
const dateText = parseDate(rawDateText);
|
|
174
|
+
entries.push({ name, date: dateText });
|
|
166
175
|
});
|
|
176
|
+
if (totalEntries === 0) {
|
|
177
|
+
return {
|
|
178
|
+
status: isPrivate ? "private" : "ng",
|
|
179
|
+
reason: isPrivate ? "achievements_private" : "structure_mismatch",
|
|
180
|
+
lodestone: characterUrl,
|
|
181
|
+
achievements: { [category]: [] }
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (!foundAny) {
|
|
185
|
+
return {
|
|
186
|
+
status: "ng",
|
|
187
|
+
reason: "structure_mismatch",
|
|
188
|
+
lodestone: characterUrl,
|
|
189
|
+
achievements: { [category]: [] }
|
|
190
|
+
};
|
|
191
|
+
}
|
|
167
192
|
return {
|
|
168
|
-
status:
|
|
169
|
-
|
|
193
|
+
status: "ok",
|
|
194
|
+
lodestone: characterUrl,
|
|
195
|
+
achievements: {
|
|
196
|
+
[category]: entries
|
|
197
|
+
}
|
|
170
198
|
};
|
|
171
199
|
}
|
|
200
|
+
function extractAchievementDetailLinks(html) {
|
|
201
|
+
const $ = cheerio.load(html);
|
|
202
|
+
const map = new Map();
|
|
203
|
+
$("li.entry").each((_, el) => {
|
|
204
|
+
const entry = $(el);
|
|
205
|
+
const name = entry.find("p.entry__activity__txt").first().text().trim();
|
|
206
|
+
if (!name || map.has(name))
|
|
207
|
+
return;
|
|
208
|
+
const href = entry
|
|
209
|
+
.find("a")
|
|
210
|
+
.map((_, a) => $(a).attr("href"))
|
|
211
|
+
.get()
|
|
212
|
+
.find((h) => h && h.includes("/achievement/detail/"));
|
|
213
|
+
if (!href)
|
|
214
|
+
return;
|
|
215
|
+
const link = href.startsWith("http") ? href : `${LODESTONE_BASE_URL}${href}`;
|
|
216
|
+
map.set(name, link.split("#")[0]);
|
|
217
|
+
});
|
|
218
|
+
return map;
|
|
219
|
+
}
|
|
220
|
+
function parseAchievementRequirementFromDetailHtml(html) {
|
|
221
|
+
const $ = cheerio.load(html);
|
|
222
|
+
const text = $("p.achievement__base--text").first().text().trim();
|
|
223
|
+
return text || null;
|
|
224
|
+
}
|
|
225
|
+
async function fetchAchievementRequirement(detailUrl) {
|
|
226
|
+
const html = await fetchAchievementCategoryHtml(detailUrl);
|
|
227
|
+
return parseAchievementRequirementFromDetailHtml(html);
|
|
228
|
+
}
|
|
172
229
|
/**
|
|
173
|
-
*
|
|
230
|
+
* キャラクターURLとカテゴリを指定して、アチーブを取得して返します。
|
|
174
231
|
*/
|
|
175
|
-
function
|
|
176
|
-
|
|
232
|
+
async function fetchAchievementDetailsByCategory(characterUrl, category) {
|
|
233
|
+
const categoryId = getAchievementCategoryId(category);
|
|
234
|
+
if (!categoryId) {
|
|
235
|
+
return {
|
|
236
|
+
status: "ng",
|
|
237
|
+
reason: "unknown_category",
|
|
238
|
+
lodestone: characterUrl,
|
|
239
|
+
achievements: { [category]: [] }
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const achievementUrl = buildAchievementCategoryUrlByCategory(characterUrl, category);
|
|
243
|
+
if (!achievementUrl) {
|
|
244
|
+
return {
|
|
245
|
+
status: "ng",
|
|
246
|
+
reason: "invalid_character_url",
|
|
247
|
+
lodestone: characterUrl,
|
|
248
|
+
achievements: { [category]: [] }
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const html = await fetchAchievementCategoryHtml(achievementUrl);
|
|
253
|
+
const parsed = parseAchievementDetailsFromHtml(html, characterUrl, category);
|
|
254
|
+
if (parsed.status !== "ok")
|
|
255
|
+
return parsed;
|
|
256
|
+
const detailLinks = extractAchievementDetailLinks(html);
|
|
257
|
+
const entries = parsed.achievements[category] ?? [];
|
|
258
|
+
let cursor = 0;
|
|
259
|
+
const concurrency = 5;
|
|
260
|
+
async function worker() {
|
|
261
|
+
while (cursor < entries.length) {
|
|
262
|
+
const index = cursor++;
|
|
263
|
+
const entry = entries[index];
|
|
264
|
+
if (!entry || entry.requirement !== undefined)
|
|
265
|
+
continue;
|
|
266
|
+
const link = detailLinks.get(entry.name);
|
|
267
|
+
if (!link) {
|
|
268
|
+
entry.requirement = null;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
entry.requirement = await fetchAchievementRequirement(link);
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
entry.requirement = null;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
|
280
|
+
return parsed;
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
if (axios_1.default.isAxiosError(error) && error.response?.status === 403) {
|
|
284
|
+
return {
|
|
285
|
+
status: "private",
|
|
286
|
+
reason: "http_403",
|
|
287
|
+
lodestone: characterUrl,
|
|
288
|
+
achievements: { [category]: [] }
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
status: "ng",
|
|
293
|
+
reason: "http_error",
|
|
294
|
+
lodestone: characterUrl,
|
|
295
|
+
achievements: { [category]: [] }
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 全カテゴリのアチーブを取得して返します。
|
|
301
|
+
*/
|
|
302
|
+
async function fetchAllAchievementDetails(characterUrl) {
|
|
303
|
+
const categories = getAchievementCategoryDefinitions().map((c) => c.category);
|
|
304
|
+
const result = {
|
|
305
|
+
status: "ok",
|
|
306
|
+
lodestone: characterUrl,
|
|
307
|
+
achievements: {}
|
|
308
|
+
};
|
|
309
|
+
for (const category of categories) {
|
|
310
|
+
const data = await fetchAchievementDetailsByCategory(characterUrl, category);
|
|
311
|
+
result.achievements[category] = data.achievements[category] ?? [];
|
|
312
|
+
if (data.status !== "ok" && result.status === "ok") {
|
|
313
|
+
result.status = data.status;
|
|
314
|
+
result.reason = data.reason;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return result;
|
|
177
318
|
}
|
|
178
319
|
/**
|
|
179
320
|
* Lodestone のアチーブメント一覧ページを取得します。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lodestoneAchievements.js","sourceRoot":"","sources":["../src/lodestoneAchievements.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"lodestoneAchievements.js","sourceRoot":"","sources":["../src/lodestoneAchievements.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piyoraik/ffxiv-lodestone-character-lookup",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "FFXIV Lodestone character lookup (search → character URL) and high-end achievement checks",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "",
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "tsc -p tsconfig.json && node scripts/copy-data.cjs",
|
|
30
30
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
31
|
-
"prepublishOnly": "npm run build"
|
|
31
|
+
"prepublishOnly": "npm run build",
|
|
32
|
+
"test": "npm run build && node --test test/achievements.test.js && node test/integration-runner.js",
|
|
33
|
+
"update-readme-requirements": "node scripts/update-readme-requirements.cjs"
|
|
32
34
|
},
|
|
33
35
|
"dependencies": {
|
|
34
36
|
"axios": "^1.7.9",
|