@timmsy/riftjs 1.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +50 -0
- package/LICENSE +1 -1
- package/README.md +192 -0
- package/endpoints/datadragon.js +40 -0
- package/endpoints/riot.js +176 -0
- package/index.js +55 -41
- package/package.json +15 -2
- package/test-endpoints.js +65 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Publish package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
packages: write
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Setup Node
|
|
20
|
+
uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: 20
|
|
23
|
+
registry-url: https://registry.npmjs.org
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: npm ci
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: npm test
|
|
30
|
+
|
|
31
|
+
- name: Validate tag matches package version
|
|
32
|
+
run: |
|
|
33
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
34
|
+
PKG_VERSION="$(node -p "require('./package.json').version")"
|
|
35
|
+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
|
|
36
|
+
echo "Tag version v$TAG_VERSION does not match package.json version $PKG_VERSION"
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
- name: Publish to npm
|
|
41
|
+
run: npm publish --access public
|
|
42
|
+
env:
|
|
43
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
44
|
+
|
|
45
|
+
- name: Publish to GitHub Packages
|
|
46
|
+
run: |
|
|
47
|
+
npm config set @timmsy:registry https://npm.pkg.github.com
|
|
48
|
+
npm publish --registry=https://npm.pkg.github.com
|
|
49
|
+
env:
|
|
50
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/LICENSE
CHANGED
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# RiftJS
|
|
2
|
+
|
|
3
|
+
A lightweight Node.js wrapper for the Riot Games API, providing easy access to League of Legends game data.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@timmsy/riftjs)
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
RiftJS simplifies interaction with the Riot Games API and DataDragon static data. It supports fetching account details, summoner info, match history, and game data using a modular endpoint structure. Built with `axios` and `dotenv`, it’s designed for developers building League of Legends tools or applications.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **RiotAPI**: Fetch account data by Riot ID, summoner data by PUUID, match history, and match details.
|
|
15
|
+
- **DataDragon**: Access static game data like champions and items.
|
|
16
|
+
- **Region Support**: Handles platform (e.g., `EUW1`) and shard (e.g., `europe`) routing.
|
|
17
|
+
- **Modular Design**: Endpoints are organized in an `/endpoints/` directory for easy extension.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Install RiftJS via npm:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @timmsy/riftjs
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
You’ll also need a Riot Games API key from [developer.riotgames.com](https://developer.riotgames.com/).
|
|
28
|
+
|
|
29
|
+
## Setup
|
|
30
|
+
|
|
31
|
+
1. **Install Dependencies**:
|
|
32
|
+
Ensure you have Node.js installed, then run the install command above.
|
|
33
|
+
|
|
34
|
+
2. **Configure Environment**:
|
|
35
|
+
Create a `.env` file in your project root with your API key and region:
|
|
36
|
+
|
|
37
|
+
```env
|
|
38
|
+
RIOT_API_KEY=RGAPI-your-api-key-here
|
|
39
|
+
REGION=EUW1
|
|
40
|
+
TEST_RIOT_ID=YourRiotName
|
|
41
|
+
TEST_TAG_LINE=EUW
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Replace `RGAPI-your-api-key-here` with your API key.
|
|
45
|
+
- Use a short region code (e.g., `EUW1`, `NA1`). See [Region Mapping](#region-mapping) for details.
|
|
46
|
+
- `TEST_RIOT_ID` and `TEST_TAG_LINE` are used by the endpoint test script.
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
Here’s a basic example to get started:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
const { RiotAPI, DataDragon } = require('@timmsy/riftjs');
|
|
54
|
+
|
|
55
|
+
// Initialize RiotAPI
|
|
56
|
+
const riot = new RiotAPI();
|
|
57
|
+
|
|
58
|
+
// Fetch account, summoner, and match data
|
|
59
|
+
async function fetchPlayerData() {
|
|
60
|
+
try {
|
|
61
|
+
const account = await riot.getAccountByRiotId('Timmsy#BRUV');
|
|
62
|
+
console.log('Account:', account);
|
|
63
|
+
|
|
64
|
+
const summoner = await riot.getSummonerByPuuid(account.puuid);
|
|
65
|
+
console.log('Summoner:', summoner);
|
|
66
|
+
|
|
67
|
+
const matchlist = await riot.getMatchlistByPuuid(account.puuid, { start: 0, count: 5 });
|
|
68
|
+
console.log('Matchlist:', matchlist);
|
|
69
|
+
|
|
70
|
+
const match = await riot.getMatchById(matchlist[0]);
|
|
71
|
+
console.log('Match:', match);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error('Error:', error.message);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Initialize DataDragon
|
|
78
|
+
const dd = new DataDragon();
|
|
79
|
+
|
|
80
|
+
async function fetchStaticData() {
|
|
81
|
+
try {
|
|
82
|
+
const champions = await dd.getChampions();
|
|
83
|
+
console.log('Champions:', champions.data);
|
|
84
|
+
|
|
85
|
+
const items = await dd.getItems();
|
|
86
|
+
console.log('Items:', items.data);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error('Error:', error.message);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Run the examples
|
|
93
|
+
fetchPlayerData();
|
|
94
|
+
fetchStaticData();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## API Reference
|
|
98
|
+
|
|
99
|
+
### RiotAPI
|
|
100
|
+
|
|
101
|
+
- `getAccountByRiotId(riotId, [tagLine], [region])`: Fetch account by Riot ID (e.g., `Timmsy#BRUV`).
|
|
102
|
+
- `getSummonerByPuuid(puuid, [region])`: Get summoner data by PUUID.
|
|
103
|
+
- `getMatchlistByPuuid(puuid, [options], [region])`: Get match history (options: `{ start, count }`).
|
|
104
|
+
- `getMatchById(matchId, [region])`: Get full match payload (`metadata` + `info`) by match ID.
|
|
105
|
+
- `getMatchTimelineById(matchId, [region])`: Get timeline payload by match ID.
|
|
106
|
+
- `getMatchlistByPuuidAll(puuid, [options], [region], [pacing])`: Fetch all match IDs in pages of 100.
|
|
107
|
+
- `getMatchesWithDetailsByPuuid(puuid, [options], [region], [pacing])`: Fetch all match IDs and their match payloads.
|
|
108
|
+
|
|
109
|
+
`options` supports Riot Match-V5 query params:
|
|
110
|
+
- `startTime` (epoch seconds)
|
|
111
|
+
- `endTime` (epoch seconds)
|
|
112
|
+
- `queue` (int)
|
|
113
|
+
- `type` (string)
|
|
114
|
+
- `start` (int, default `0`)
|
|
115
|
+
- `count` (int, `0-100`, single-page method only)
|
|
116
|
+
|
|
117
|
+
`pacing` helps respect rate limits on multi-request methods:
|
|
118
|
+
- `getMatchlistByPuuidAll`: `{ delayMs, maxMatches }`
|
|
119
|
+
- `getMatchesWithDetailsByPuuid`: `{ pageDelayMs, detailDelayMs, maxMatches }`
|
|
120
|
+
|
|
121
|
+
### DataDragon
|
|
122
|
+
|
|
123
|
+
- `getChampions()`: Fetch all champion data.
|
|
124
|
+
- `getItems()`: Fetch all item data.
|
|
125
|
+
- `new DataDragon()` resolves the latest Data Dragon patch automatically.
|
|
126
|
+
- `new DataDragon('x.y.z')` pins requests to an explicit patch version.
|
|
127
|
+
|
|
128
|
+
## Region Mapping
|
|
129
|
+
|
|
130
|
+
RiftJS uses a region map to route requests correctly:
|
|
131
|
+
|
|
132
|
+
- **Platform Routing** (e.g., Summoner V4):
|
|
133
|
+
|
|
134
|
+
- `EUW1` → `euw1.api.riotgames.com`
|
|
135
|
+
- `NA1` → `na1.api.riotgames.com`
|
|
136
|
+
|
|
137
|
+
- **Shard Routing** (e.g., Account V1, Match V5):
|
|
138
|
+
|
|
139
|
+
- `EUW1` → `europe.api.riotgames.com`
|
|
140
|
+
- `NA1` → `americas.api.riotgames.com`
|
|
141
|
+
|
|
142
|
+
Supported regions: `BR1`, `EUN1`, `EUW1`, `JP1`, `KR`, `LA1`, `LA2`, `NA1`, `OC1`, `TR1`, `RU`, `PH2`, `SG2`, `TH2`, `TW2`, `VN2`.
|
|
143
|
+
|
|
144
|
+
## Keywords
|
|
145
|
+
|
|
146
|
+
- riot-api
|
|
147
|
+
- league-of-legends
|
|
148
|
+
- lol-api
|
|
149
|
+
- datadragon
|
|
150
|
+
- summoner
|
|
151
|
+
- match-history
|
|
152
|
+
- game-data
|
|
153
|
+
- node-js
|
|
154
|
+
- javascript
|
|
155
|
+
- api-wrapper
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
To contribute or run locally:
|
|
160
|
+
|
|
161
|
+
1. Clone the repo:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
git clone https://github.com/timmsy1998/RiftJS.git
|
|
165
|
+
cd RiftJS
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
2. Install dependencies:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm install
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
3. Create a `.env` file (see [Setup](#setup)).
|
|
175
|
+
4. Run endpoint checks:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
npm test
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- Riot API calls run when `TEST_RIOT_ID` is set.
|
|
182
|
+
- Data Dragon calls always run.
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT License © 2025 James Timms. See [LICENSE](LICENSE) for details.
|
|
187
|
+
|
|
188
|
+
## Links
|
|
189
|
+
|
|
190
|
+
- **npm Registry**: [https://www.npmjs.com/package/@timmsy/riftjs](https://www.npmjs.com/package/@timmsy/riftjs)
|
|
191
|
+
- **GitHub Repository**: [https://github.com/timmsy1998/RiftJS](https://github.com/timmsy1998/RiftJS)
|
|
192
|
+
- **Riot Developer Portal**: [https://developer.riotgames.com/](https://developer.riotgames.com/)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
|
|
3
|
+
module.exports = (baseURLOrResolver) => {
|
|
4
|
+
const resolveBaseURL = async () => {
|
|
5
|
+
if (typeof baseURLOrResolver === 'function') {
|
|
6
|
+
return baseURLOrResolver();
|
|
7
|
+
}
|
|
8
|
+
return baseURLOrResolver;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
/**
|
|
13
|
+
* Fetch champion metadata for the configured Data Dragon version/locale.
|
|
14
|
+
* @returns {Promise<object>} Champion data.
|
|
15
|
+
*/
|
|
16
|
+
async getChampions() {
|
|
17
|
+
try {
|
|
18
|
+
const baseURL = await resolveBaseURL();
|
|
19
|
+
const response = await axios.get(`${baseURL}/champion.json`);
|
|
20
|
+
return response.data;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new Error(`DataDragon error: ${error.message}`);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Fetch item metadata for the configured Data Dragon version/locale.
|
|
28
|
+
* @returns {Promise<object>} Item data.
|
|
29
|
+
*/
|
|
30
|
+
async getItems() {
|
|
31
|
+
try {
|
|
32
|
+
const baseURL = await resolveBaseURL();
|
|
33
|
+
const response = await axios.get(`${baseURL}/item.json`);
|
|
34
|
+
return response.data;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
throw new Error(`DataDragon error: ${error.message}`);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2
|
+
|
|
3
|
+
module.exports = (client, defaultRegion, regionMap) => ({
|
|
4
|
+
/**
|
|
5
|
+
* Fetch account data by Riot ID.
|
|
6
|
+
* @param {string} riotId - Riot ID (e.g., "Timmsy#BRUV" or "Timmsy", "BRUV").
|
|
7
|
+
* @param {string} [tagLine] - Optional tagLine if not included in riotId.
|
|
8
|
+
* @param {string} [region] - Region code used to resolve shard routing.
|
|
9
|
+
* @returns {Promise<object>} Account payload including `puuid`.
|
|
10
|
+
*/
|
|
11
|
+
async getAccountByRiotId(riotId, tagLine = null, region = defaultRegion) {
|
|
12
|
+
let gameName, tag;
|
|
13
|
+
if (riotId.includes('#')) {
|
|
14
|
+
[gameName, tag] = riotId.split('#');
|
|
15
|
+
} else {
|
|
16
|
+
gameName = riotId;
|
|
17
|
+
tag = tagLine || '';
|
|
18
|
+
}
|
|
19
|
+
if (!tag) throw new Error('TagLine is required for getAccountByRiotId');
|
|
20
|
+
const shard = regionMap[region].shard;
|
|
21
|
+
try {
|
|
22
|
+
const response = await client.get(`/riot/account/v1/accounts/by-riot-id/${encodeURIComponent(gameName)}/${encodeURIComponent(tag)}`, {
|
|
23
|
+
baseURL: `https://${shard}`,
|
|
24
|
+
});
|
|
25
|
+
return response.data;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
throw this._handleError(error);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Fetch Summoner-V4 data for a player's PUUID.
|
|
33
|
+
* @param {string} puuid - Encrypted PUUID.
|
|
34
|
+
* @param {string} [region] - Region code used to resolve platform routing.
|
|
35
|
+
* @returns {Promise<object>} Summoner payload.
|
|
36
|
+
*/
|
|
37
|
+
async getSummonerByPuuid(puuid, region = defaultRegion) {
|
|
38
|
+
const platform = regionMap[region].platform;
|
|
39
|
+
try {
|
|
40
|
+
const response = await client.get(`/lol/summoner/v4/summoners/by-puuid/${encodeURIComponent(puuid)}`, {
|
|
41
|
+
baseURL: `https://${platform}`,
|
|
42
|
+
});
|
|
43
|
+
return response.data;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw this._handleError(error);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Fetch match IDs for a PUUID from Match-V5.
|
|
51
|
+
* @param {string} puuid - Player PUUID.
|
|
52
|
+
* @param {object} [options] - Query params such as `start` and `count`.
|
|
53
|
+
* @param {string} [region] - Region code used to resolve shard routing.
|
|
54
|
+
* @returns {Promise<string[]>} Array of match IDs.
|
|
55
|
+
*/
|
|
56
|
+
async getMatchlistByPuuid(puuid, options = {}, region = defaultRegion) {
|
|
57
|
+
const shard = regionMap[region].shard;
|
|
58
|
+
try {
|
|
59
|
+
const response = await client.get(`/lol/match/v5/matches/by-puuid/${encodeURIComponent(puuid)}/ids`, {
|
|
60
|
+
baseURL: `https://${shard}`,
|
|
61
|
+
params: options,
|
|
62
|
+
});
|
|
63
|
+
return response.data;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
throw this._handleError(error);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Fetch full match details by match ID.
|
|
71
|
+
* @param {string} matchId - Match ID (e.g., "EUW1_1234567890").
|
|
72
|
+
* @param {string} [region] - Region code used to resolve shard routing.
|
|
73
|
+
* @returns {Promise<object>} Match payload.
|
|
74
|
+
*/
|
|
75
|
+
async getMatchById(matchId, region = defaultRegion) {
|
|
76
|
+
const shard = regionMap[region].shard;
|
|
77
|
+
try {
|
|
78
|
+
const response = await client.get(`/lol/match/v5/matches/${matchId}`, {
|
|
79
|
+
baseURL: `https://${shard}`,
|
|
80
|
+
});
|
|
81
|
+
return response.data;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw this._handleError(error);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Fetch match timeline data by match ID.
|
|
89
|
+
* @param {string} matchId - Match ID (e.g., "EUW1_1234567890").
|
|
90
|
+
* @param {string} [region] - Region code used to resolve shard routing.
|
|
91
|
+
* @returns {Promise<object>} Match timeline payload.
|
|
92
|
+
*/
|
|
93
|
+
async getMatchTimelineById(matchId, region = defaultRegion) {
|
|
94
|
+
const shard = regionMap[region].shard;
|
|
95
|
+
try {
|
|
96
|
+
const response = await client.get(`/lol/match/v5/matches/${matchId}/timeline`, {
|
|
97
|
+
baseURL: `https://${shard}`,
|
|
98
|
+
});
|
|
99
|
+
return response.data;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
throw this._handleError(error);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Fetch all match IDs for a PUUID using Riot's max page size (100).
|
|
107
|
+
* @param {string} puuid - Player PUUID.
|
|
108
|
+
* @param {object} [options] - Riot filters: startTime, endTime, queue, type, start.
|
|
109
|
+
* @param {string} [region] - Region code used to resolve shard routing.
|
|
110
|
+
* @param {object} [pacing] - Pacing controls.
|
|
111
|
+
* @param {number} [pacing.delayMs=1250] - Delay between page requests.
|
|
112
|
+
* @param {number|null} [pacing.maxMatches=null] - Optional cap on total IDs returned.
|
|
113
|
+
* @returns {Promise<string[]>} All matched IDs.
|
|
114
|
+
*/
|
|
115
|
+
async getMatchlistByPuuidAll(puuid, options = {}, region = defaultRegion, pacing = {}) {
|
|
116
|
+
const baseStart = Number.isInteger(options.start) ? options.start : 0;
|
|
117
|
+
const pageDelayMs = Number.isInteger(pacing.delayMs) ? pacing.delayMs : 1250;
|
|
118
|
+
const maxMatches = Number.isInteger(pacing.maxMatches) && pacing.maxMatches >= 0 ? pacing.maxMatches : null;
|
|
119
|
+
const filters = {
|
|
120
|
+
startTime: options.startTime,
|
|
121
|
+
endTime: options.endTime,
|
|
122
|
+
queue: options.queue,
|
|
123
|
+
type: options.type,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
let start = baseStart;
|
|
127
|
+
const allMatchIds = [];
|
|
128
|
+
|
|
129
|
+
while (true) {
|
|
130
|
+
const remaining = maxMatches === null ? 100 : Math.min(100, maxMatches - allMatchIds.length);
|
|
131
|
+
if (remaining <= 0) break;
|
|
132
|
+
|
|
133
|
+
const page = await this.getMatchlistByPuuid(puuid, { ...filters, start, count: remaining }, region);
|
|
134
|
+
allMatchIds.push(...page);
|
|
135
|
+
|
|
136
|
+
if (page.length < remaining) break;
|
|
137
|
+
start += page.length;
|
|
138
|
+
if (pageDelayMs > 0) await sleep(pageDelayMs);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return allMatchIds;
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Fetch match IDs and full match payloads for each ID.
|
|
146
|
+
* @param {string} puuid - Player PUUID.
|
|
147
|
+
* @param {object} [options] - Riot filters: startTime, endTime, queue, type, start.
|
|
148
|
+
* @param {string} [region] - Region code used to resolve shard routing.
|
|
149
|
+
* @param {object} [pacing] - Pacing controls.
|
|
150
|
+
* @param {number} [pacing.pageDelayMs=1250] - Delay between matchlist page requests.
|
|
151
|
+
* @param {number} [pacing.detailDelayMs=1250] - Delay between match detail requests.
|
|
152
|
+
* @param {number|null} [pacing.maxMatches=null] - Optional cap on total matches fetched.
|
|
153
|
+
* @returns {Promise<{matchIds: string[], matches: object[]}>} IDs and full match payloads.
|
|
154
|
+
*/
|
|
155
|
+
async getMatchesWithDetailsByPuuid(puuid, options = {}, region = defaultRegion, pacing = {}) {
|
|
156
|
+
const pageDelayMs = Number.isInteger(pacing.pageDelayMs) ? pacing.pageDelayMs : 1250;
|
|
157
|
+
const detailDelayMs = Number.isInteger(pacing.detailDelayMs) ? pacing.detailDelayMs : 1250;
|
|
158
|
+
const maxMatches = Number.isInteger(pacing.maxMatches) && pacing.maxMatches >= 0 ? pacing.maxMatches : null;
|
|
159
|
+
const matchIds = await this.getMatchlistByPuuidAll(
|
|
160
|
+
puuid,
|
|
161
|
+
options,
|
|
162
|
+
region,
|
|
163
|
+
{ delayMs: pageDelayMs, maxMatches }
|
|
164
|
+
);
|
|
165
|
+
const matches = [];
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < matchIds.length; i += 1) {
|
|
168
|
+
matches.push(await this.getMatchById(matchIds[i], region));
|
|
169
|
+
if (detailDelayMs > 0 && i < matchIds.length - 1) {
|
|
170
|
+
await sleep(detailDelayMs);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { matchIds, matches };
|
|
175
|
+
},
|
|
176
|
+
});
|
package/index.js
CHANGED
|
@@ -1,41 +1,42 @@
|
|
|
1
1
|
require('dotenv').config();
|
|
2
2
|
const axios = require('axios');
|
|
3
|
+
const riotEndpoints = require('./endpoints/riot');
|
|
4
|
+
const dataDragonEndpoints = require('./endpoints/datadragon');
|
|
5
|
+
|
|
6
|
+
// Maps region codes to the platform and routing shard hosts required by Riot APIs.
|
|
7
|
+
const regionMap = {
|
|
8
|
+
BR1: { platform: 'br1.api.riotgames.com', shard: 'americas.api.riotgames.com' },
|
|
9
|
+
EUN1: { platform: 'eun1.api.riotgames.com', shard: 'europe.api.riotgames.com' },
|
|
10
|
+
EUW1: { platform: 'euw1.api.riotgames.com', shard: 'europe.api.riotgames.com' },
|
|
11
|
+
JP1: { platform: 'jp1.api.riotgames.com', shard: 'asia.api.riotgames.com' },
|
|
12
|
+
KR: { platform: 'kr.api.riotgames.com', shard: 'asia.api.riotgames.com' },
|
|
13
|
+
LA1: { platform: 'la1.api.riotgames.com', shard: 'americas.api.riotgames.com' },
|
|
14
|
+
LA2: { platform: 'la2.api.riotgames.com', shard: 'americas.api.riotgames.com' },
|
|
15
|
+
NA1: { platform: 'na1.api.riotgames.com', shard: 'americas.api.riotgames.com' },
|
|
16
|
+
OC1: { platform: 'oc1.api.riotgames.com', shard: 'sea.api.riotgames.com' },
|
|
17
|
+
TR1: { platform: 'tr1.api.riotgames.com', shard: 'europe.api.riotgames.com' },
|
|
18
|
+
RU: { platform: 'ru.api.riotgames.com', shard: 'europe.api.riotgames.com' },
|
|
19
|
+
PH2: { platform: 'ph2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
|
|
20
|
+
SG2: { platform: 'sg2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
|
|
21
|
+
TH2: { platform: 'th2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
|
|
22
|
+
TW2: { platform: 'tw2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
|
|
23
|
+
VN2: { platform: 'vn2.api.riotgames.com', shard: 'sea.api.riotgames.com' },
|
|
24
|
+
};
|
|
3
25
|
|
|
4
|
-
/**
|
|
5
|
-
* RiotAPI class for interacting with Riot Games API endpoints.
|
|
6
|
-
*/
|
|
7
26
|
class RiotAPI {
|
|
8
27
|
constructor() {
|
|
9
28
|
this.apiKey = process.env.RIOT_API_KEY;
|
|
10
29
|
if (!this.apiKey) throw new Error('RIOT_API_KEY is required in .env');
|
|
11
|
-
this.region = process.env.REGION || '
|
|
30
|
+
this.region = (process.env.REGION || 'EUW1').toUpperCase();
|
|
31
|
+
if (!regionMap[this.region]) throw new Error(`Invalid region: ${this.region}`);
|
|
12
32
|
this.client = axios.create({
|
|
13
|
-
baseURL: `https://${this.region}.api.riotgames.com`,
|
|
14
33
|
headers: { 'X-Riot-Token': this.apiKey },
|
|
15
34
|
});
|
|
35
|
+
// Attach endpoint methods with a shared axios client and region configuration.
|
|
36
|
+
Object.assign(this, riotEndpoints(this.client, this.region, regionMap));
|
|
16
37
|
}
|
|
17
38
|
|
|
18
|
-
|
|
19
|
-
* Get summoner data by name.
|
|
20
|
-
* @param {string} name - Summoner name.
|
|
21
|
-
* @param {string} [region] - Region (defaults to constructor region).
|
|
22
|
-
* @returns {Promise<object>} Summoner data.
|
|
23
|
-
*/
|
|
24
|
-
async getSummonerByName(name, region = this.region) {
|
|
25
|
-
try {
|
|
26
|
-
const response = await this.client.get(`/lol/summoner/v4/summoners/by-name/${encodeURIComponent(name)}`, {
|
|
27
|
-
baseURL: `https://${region}.api.riotgames.com`,
|
|
28
|
-
});
|
|
29
|
-
return response.data;
|
|
30
|
-
} catch (error) {
|
|
31
|
-
throw this._handleError(error);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Handle axios errors and provide meaningful messages.
|
|
37
|
-
* @private
|
|
38
|
-
*/
|
|
39
|
+
// Normalize axios errors to stable Error messages for consumers.
|
|
39
40
|
_handleError(error) {
|
|
40
41
|
if (error.response) {
|
|
41
42
|
const { status, data } = error.response;
|
|
@@ -47,28 +48,41 @@ class RiotAPI {
|
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
/**
|
|
51
|
-
* DataDragon class for accessing static game data.
|
|
52
|
-
*/
|
|
53
51
|
class DataDragon {
|
|
54
|
-
constructor(version =
|
|
52
|
+
constructor(version = null, locale = 'en_US') {
|
|
55
53
|
this.version = version;
|
|
56
54
|
this.locale = locale;
|
|
57
|
-
this.baseURL =
|
|
55
|
+
this.baseURL = null;
|
|
56
|
+
this._baseURLPromise = null;
|
|
57
|
+
|
|
58
|
+
// Attach static-data endpoint methods that resolve the latest version when needed.
|
|
59
|
+
Object.assign(this, dataDragonEndpoints(() => this._resolveBaseURL()));
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
async _resolveBaseURL() {
|
|
63
|
+
if (this.baseURL) return this.baseURL;
|
|
64
|
+
if (this._baseURLPromise) return this._baseURLPromise;
|
|
65
|
+
|
|
66
|
+
this._baseURLPromise = (async () => {
|
|
67
|
+
if (!this.version) {
|
|
68
|
+
const response = await axios.get('https://ddragon.leagueoflegends.com/api/versions.json');
|
|
69
|
+
const latestVersion = Array.isArray(response.data) ? response.data[0] : null;
|
|
70
|
+
if (!latestVersion) {
|
|
71
|
+
throw new Error('Could not resolve latest Data Dragon version');
|
|
72
|
+
}
|
|
73
|
+
this.version = latestVersion;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.baseURL = `https://ddragon.leagueoflegends.com/cdn/${this.version}/data/${this.locale}`;
|
|
77
|
+
return this.baseURL;
|
|
78
|
+
})();
|
|
79
|
+
|
|
65
80
|
try {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
throw new Error(`DataDragon error: ${error.message}`);
|
|
81
|
+
return await this._baseURLPromise;
|
|
82
|
+
} finally {
|
|
83
|
+
this._baseURLPromise = null;
|
|
70
84
|
}
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
|
|
74
|
-
module.exports = { RiotAPI, DataDragon };
|
|
88
|
+
module.exports = { RiotAPI, DataDragon };
|
package/package.json
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timmsy/riftjs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"test": "
|
|
6
|
+
"test": "npm run test:endpoints",
|
|
7
|
+
"test:endpoints": "node test-endpoints.js"
|
|
7
8
|
},
|
|
8
9
|
"dependencies": {
|
|
9
10
|
"axios": "^1.7.7",
|
|
10
11
|
"dotenv": "^16.4.5"
|
|
11
12
|
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"riot-api",
|
|
15
|
+
"league-of-legends",
|
|
16
|
+
"lol-api",
|
|
17
|
+
"datadragon",
|
|
18
|
+
"summoner",
|
|
19
|
+
"match-history",
|
|
20
|
+
"game-data",
|
|
21
|
+
"node-js",
|
|
22
|
+
"javascript",
|
|
23
|
+
"api-wrapper"
|
|
24
|
+
],
|
|
12
25
|
"author": "James Timms",
|
|
13
26
|
"description": "A lightweight Node.js wrapper for the Riot Games API, providing easy access to League of Legends game data.",
|
|
14
27
|
"license": "MIT",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
const { RiotAPI, DataDragon } = require('./index');
|
|
3
|
+
|
|
4
|
+
async function run() {
|
|
5
|
+
const riotId = process.env.TEST_RIOT_ID;
|
|
6
|
+
const tagLine = process.env.TEST_TAG_LINE;
|
|
7
|
+
const dd = new DataDragon();
|
|
8
|
+
|
|
9
|
+
if (process.env.RIOT_API_KEY && riotId) {
|
|
10
|
+
const riot = new RiotAPI();
|
|
11
|
+
const account = await riot.getAccountByRiotId(riotId, tagLine);
|
|
12
|
+
console.log('[PASS] getAccountByRiotId:', account.puuid);
|
|
13
|
+
|
|
14
|
+
const summoner = await riot.getSummonerByPuuid(account.puuid);
|
|
15
|
+
console.log('[PASS] getSummonerByPuuid:', {
|
|
16
|
+
puuid: summoner.puuid,
|
|
17
|
+
summonerLevel: summoner.summonerLevel,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const matchIds = await riot.getMatchlistByPuuid(account.puuid, { start: 0, count: 3 });
|
|
21
|
+
console.log('[PASS] getMatchlistByPuuid:', matchIds.length, 'match ids');
|
|
22
|
+
|
|
23
|
+
if (matchIds.length > 0) {
|
|
24
|
+
const match = await riot.getMatchById(matchIds[0]);
|
|
25
|
+
console.log('[PASS] getMatchById:', match.metadata.matchId);
|
|
26
|
+
|
|
27
|
+
const timeline = await riot.getMatchTimelineById(matchIds[0]);
|
|
28
|
+
console.log('[PASS] getMatchTimelineById:', timeline.metadata.matchId);
|
|
29
|
+
|
|
30
|
+
const allMatchIds = await riot.getMatchlistByPuuidAll(
|
|
31
|
+
account.puuid,
|
|
32
|
+
{ start: 0 },
|
|
33
|
+
undefined,
|
|
34
|
+
{ maxMatches: 2, delayMs: 0 }
|
|
35
|
+
);
|
|
36
|
+
console.log('[PASS] getMatchlistByPuuidAll:', allMatchIds.length, 'match ids');
|
|
37
|
+
|
|
38
|
+
const withDetails = await riot.getMatchesWithDetailsByPuuid(
|
|
39
|
+
account.puuid,
|
|
40
|
+
{ start: 0 },
|
|
41
|
+
undefined,
|
|
42
|
+
{ maxMatches: 2, pageDelayMs: 0, detailDelayMs: 0 }
|
|
43
|
+
);
|
|
44
|
+
console.log('[PASS] getMatchesWithDetailsByPuuid:', withDetails.matches.length, 'matches');
|
|
45
|
+
} else {
|
|
46
|
+
console.log('[SKIP] Match-by-id/timeline/all/details checks: no matches returned');
|
|
47
|
+
}
|
|
48
|
+
} else if (process.env.RIOT_API_KEY) {
|
|
49
|
+
console.log('[SKIP] Riot endpoints: set TEST_RIOT_ID (and TEST_TAG_LINE if needed)');
|
|
50
|
+
} else {
|
|
51
|
+
console.log('[SKIP] Riot endpoints: set RIOT_API_KEY and TEST_RIOT_ID');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const champions = await dd.getChampions();
|
|
55
|
+
console.log('[PASS] getChampions:', Object.keys(champions.data || {}).length, 'champions');
|
|
56
|
+
console.log('[INFO] DataDragon version:', dd.version);
|
|
57
|
+
|
|
58
|
+
const items = await dd.getItems();
|
|
59
|
+
console.log('[PASS] getItems:', Object.keys(items.data || {}).length, 'items');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
run().catch((error) => {
|
|
63
|
+
console.error('[FAIL]', error.message);
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
});
|