clashofclans.js 2.0.0 → 2.0.1-dev.0dd451f
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/CHANGELOG.md +21 -4
- package/dist/client/EventManager.js +4 -4
- package/dist/rest/RequestHandler.js +25 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,16 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## 2.0.1 (2021-11-27)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- IP retrieval method and Event Loop ([#70](https://github.com/clashperk/clashofclans.js/issues/70)) ([82b84ba](https://github.com/clashperk/clashofclans.js/commit/82b84ba5d96505c43b75e53aa07f547ef0b77778))
|
|
10
|
+
|
|
5
11
|
## 2.0.0 (2021-11-26)
|
|
6
12
|
|
|
7
13
|
This new version is a complete TypeScript rewrite to convert everything from plain (literal JSON) objects to class (constructor) objects and support a lot more features.
|
|
8
14
|
|
|
9
15
|
### Features
|
|
10
16
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
17
|
+
- HTTP Request Request Retries ([#26](https://github.com/clashperk/clashofclans.js/issues/26)) ([94585f3](https://github.com/clashperk/clashofclans.js/commit/94585f3a84a7175b2d07872f9eb9e42372b95e12))
|
|
18
|
+
- Event Manager and Custom Events ([#37](https://github.com/clashperk/clashofclans.js/issues/37)) ([5027ae6](https://github.com/clashperk/clashofclans.js/commit/5027ae663a8e07175e17384c7e5706f4a1a7afb4))
|
|
13
19
|
- Email Password Login ([#31](https://github.com/clashperk/clashofclans.js/issues/31)) ([4153cd3](https://github.com/clashperk/clashofclans.js/commit/4153cd37ea0e1c71550b9e892105b84d5a407e23))
|
|
14
|
-
-
|
|
20
|
+
- Queue Throttler and Batch Throttler ([#34](https://github.com/clashperk/clashofclans.js/issues/34)) ([3a8f051](https://github.com/clashperk/clashofclans.js/commit/3a8f051552e93b98f89bc7d524acdecddf242718))
|
|
15
21
|
- Override Request Options ([#36](https://github.com/clashperk/clashofclans.js/issues/36)) ([42d7fdd](https://github.com/clashperk/clashofclans.js/commit/42d7fdd36262cc46f23b731f8cffb9daea19d3b0))
|
|
16
22
|
- Internal Caching Options ([#53](https://github.com/clashperk/clashofclans.js/issues/53)) ([984451d](https://github.com/clashperk/clashofclans.js/commit/30ea3240c11866008d0dae514468c0fdbb34ffd0))
|
|
17
|
-
-
|
|
23
|
+
- Additional Properties for Player Units ([#65](https://github.com/clashperk/clashofclans.js/pull/65)) ([aa1696](https://github.com/clashperk/clashofclans.js/commit/aa1696243d96d4fed0250b4282c60522a6482343))
|
|
24
|
+
|
|
25
|
+
### Links
|
|
26
|
+
|
|
27
|
+
- Documentation
|
|
28
|
+
https://clashofclans.js.org/docs
|
|
29
|
+
|
|
30
|
+
- Guide
|
|
31
|
+
https://clashofclans.js.org/guide
|
|
32
|
+
|
|
33
|
+
- Updating to v2.0.0
|
|
34
|
+
https://clashofclans.js.org/docs/updating-to-v2
|
|
@@ -163,7 +163,7 @@ class EventManager {
|
|
|
163
163
|
const end = Util_1.Util.getSeasonEndTime().getTime() - Date.now();
|
|
164
164
|
// Why this? setTimeout can be up to 24.8 days or 2147483647ms [(2^31 - 1) Max 32bit Integer]
|
|
165
165
|
if (end > 24 * 60 * 60 * 1000) {
|
|
166
|
-
setTimeout(this.seasonEndHandler.bind(this), 60 * 60 * 1000)
|
|
166
|
+
setTimeout(this.seasonEndHandler.bind(this), 60 * 60 * 1000);
|
|
167
167
|
}
|
|
168
168
|
else if (end > 0) {
|
|
169
169
|
setTimeout(() => {
|
|
@@ -176,21 +176,21 @@ class EventManager {
|
|
|
176
176
|
for (const tag of this._clanTags)
|
|
177
177
|
await this.runClanUpdate(tag);
|
|
178
178
|
this.client.emit(Constants_1.EVENTS.CLAN_LOOP_END);
|
|
179
|
-
setTimeout(this.clanUpdateHandler.bind(this), 10000)
|
|
179
|
+
setTimeout(this.clanUpdateHandler.bind(this), 10000);
|
|
180
180
|
}
|
|
181
181
|
async playerUpdateHandler() {
|
|
182
182
|
this.client.emit(Constants_1.EVENTS.PLAYER_LOOP_START);
|
|
183
183
|
for (const tag of this._playerTags)
|
|
184
184
|
await this.runPlayerUpdate(tag);
|
|
185
185
|
this.client.emit(Constants_1.EVENTS.PLAYER_LOOP_END);
|
|
186
|
-
setTimeout(this.playerUpdateHandler.bind(this), 10000)
|
|
186
|
+
setTimeout(this.playerUpdateHandler.bind(this), 10000);
|
|
187
187
|
}
|
|
188
188
|
async warUpdateHandler() {
|
|
189
189
|
this.client.emit(Constants_1.EVENTS.WAR_LOOP_START);
|
|
190
190
|
for (const tag of this._warTags)
|
|
191
191
|
await this.runWarUpdate(tag);
|
|
192
192
|
this.client.emit(Constants_1.EVENTS.WAR_LOOP_END);
|
|
193
|
-
setTimeout(this.warUpdateHandler.bind(this), 10000)
|
|
193
|
+
setTimeout(this.warUpdateHandler.bind(this), 10000);
|
|
194
194
|
}
|
|
195
195
|
async runClanUpdate(tag) {
|
|
196
196
|
if (this._inMaintenance)
|
|
@@ -21,6 +21,7 @@ const HTTPError_1 = require("./HTTPError");
|
|
|
21
21
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
22
22
|
const https_1 = __importDefault(require("https"));
|
|
23
23
|
const keyv_1 = __importDefault(require("keyv"));
|
|
24
|
+
const IP_REGEX = /\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}/g;
|
|
24
25
|
const agent = new https_1.default.Agent({ keepAlive: true });
|
|
25
26
|
/** Represents a Request Handler. */
|
|
26
27
|
class RequestHandler {
|
|
@@ -110,7 +111,7 @@ class RequestHandler {
|
|
|
110
111
|
if (res?.status === 403) {
|
|
111
112
|
const index = this.keys.indexOf(key);
|
|
112
113
|
this.keys.splice(index, 1);
|
|
113
|
-
|
|
114
|
+
process.emitWarning(`Pre-defined key #${index + 1} is no longer valid. Removed from the key list.`);
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
}
|
|
@@ -121,19 +122,23 @@ class RequestHandler {
|
|
|
121
122
|
headers: { 'Content-Type': 'application/json' },
|
|
122
123
|
body: JSON.stringify({ email: this.email, password: this.password })
|
|
123
124
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
const data = await res.json();
|
|
126
|
+
if (!res.ok)
|
|
127
|
+
throw new Error(`Invalid email or password. ${JSON.stringify(data)}`);
|
|
128
|
+
const ip = await this.getIp(data.temporaryAPIToken);
|
|
129
|
+
if (!ip)
|
|
130
|
+
throw new Error('Failed to get the IP address.');
|
|
131
|
+
return this.getKeys(res.headers.get('set-cookie'), ip);
|
|
128
132
|
}
|
|
129
|
-
async getKeys(cookie) {
|
|
130
|
-
const ip = await this.getIp();
|
|
133
|
+
async getKeys(cookie, ip) {
|
|
131
134
|
const res = await (0, node_fetch_1.default)(`${Constants_1.DEV_SITE_API_BASE_URL}/apikey/list`, {
|
|
132
135
|
method: 'POST',
|
|
133
136
|
timeout: 10000,
|
|
134
137
|
headers: { 'Content-Type': 'application/json', cookie }
|
|
135
138
|
});
|
|
136
139
|
const data = await res.json();
|
|
140
|
+
if (!res.ok)
|
|
141
|
+
throw new Error(`Failed to retrieve the API Keys. ${JSON.stringify(data)}`);
|
|
137
142
|
// Get all available keys from the developer site.
|
|
138
143
|
const keys = (data.keys ?? []);
|
|
139
144
|
// Revoke keys for specified key name but not matching current IP address.
|
|
@@ -157,7 +162,7 @@ class RequestHandler {
|
|
|
157
162
|
keys.push(key);
|
|
158
163
|
}
|
|
159
164
|
if (this.keys.length < this.keyCount && keys.length === 10) {
|
|
160
|
-
|
|
165
|
+
process.emitWarning(`${this.keyCount} key(s) were requested but failed to create ${this.keyCount - this.keys.length} more key(s).`);
|
|
161
166
|
}
|
|
162
167
|
if (!this.keys.length) {
|
|
163
168
|
throw new Error([
|
|
@@ -188,10 +193,20 @@ class RequestHandler {
|
|
|
188
193
|
})
|
|
189
194
|
});
|
|
190
195
|
const data = await res.json();
|
|
196
|
+
if (!res.ok)
|
|
197
|
+
throw new Error(`Failed to create API Key. ${JSON.stringify(data)}`);
|
|
191
198
|
return data.key;
|
|
192
199
|
}
|
|
193
|
-
async getIp() {
|
|
194
|
-
|
|
200
|
+
async getIp(token) {
|
|
201
|
+
try {
|
|
202
|
+
const decoded = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
|
|
203
|
+
const props = decoded.limits.find((limit) => limit.hasOwnProperty('cidrs'));
|
|
204
|
+
return props.cidrs[0].match(IP_REGEX)[0];
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
const body = await (0, node_fetch_1.default)('https://api.ipify.org', { timeout: 10000 }).then((res) => res.text());
|
|
208
|
+
return body.match(IP_REGEX)?.[0] ?? null;
|
|
209
|
+
}
|
|
195
210
|
}
|
|
196
211
|
}
|
|
197
212
|
exports.RequestHandler = RequestHandler;
|