clashofclans.js 2.0.0-dev.7f4d9f8 → 2.0.0-dev.86dfaef
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 +1 -2
- package/dist/client/Client.d.ts +38 -22
- package/dist/client/Client.js +47 -29
- package/dist/client/EventManager.d.ts +39 -13
- package/dist/client/EventManager.js +36 -25
- package/dist/rest/HTTPError.d.ts +20 -2
- package/dist/rest/HTTPError.js +29 -8
- package/dist/rest/RESTManager.d.ts +24 -0
- package/dist/rest/RequestHandler.d.ts +83 -2
- package/dist/rest/RequestHandler.js +40 -13
- package/dist/struct/ClanWar.d.ts +6 -4
- package/dist/struct/ClanWar.js +17 -3
- package/dist/struct/ClanWarLeagueGroup.js +2 -2
- package/dist/struct/ClanWarLog.d.ts +2 -2
- package/dist/struct/ClanWarLog.js +2 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/util/Constants.d.ts +5 -0
- package/dist/util/Constants.js +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,8 +45,7 @@ const client = new Client({
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
client.events.addClans(['#8P2QG08P']);
|
|
48
|
-
client.events.
|
|
49
|
-
type: 'CLAN',
|
|
48
|
+
client.events.setClanEvent({
|
|
50
49
|
name: 'clanDescriptionChange',
|
|
51
50
|
filter: (oldClan, newClan) => {
|
|
52
51
|
return oldClan.description !== newClan.description;
|
package/dist/client/Client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { ClanSearchOptions, SearchOptions, ClientOptions, InitOptions, OverrideOptions } from '../rest/RequestHandler';
|
|
3
|
-
import { EVENTS } from '../util/Constants';
|
|
3
|
+
import { EVENTS, CWL_ROUNDS } from '../util/Constants';
|
|
4
4
|
import { RESTManager } from '../rest/RESTManager';
|
|
5
5
|
import { EventManager } from './EventManager';
|
|
6
6
|
import { EventEmitter } from 'events';
|
|
@@ -40,12 +40,38 @@ export declare class Client extends EventEmitter {
|
|
|
40
40
|
getClanMembers(clanTag: string, options?: SearchOptions): Promise<ClanMember[]>;
|
|
41
41
|
/** Get clan war log. */
|
|
42
42
|
getClanWarLog(clanTag: string, options?: SearchOptions): Promise<ClanWarLog[]>;
|
|
43
|
-
/** Get info about currently running war (
|
|
44
|
-
getClanWar(clanTag: string, options?: OverrideOptions): Promise<ClanWar
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
/** Get info about currently running war (normal or friendly) in the clan. */
|
|
44
|
+
getClanWar(clanTag: string, options?: OverrideOptions): Promise<ClanWar>;
|
|
45
|
+
/**
|
|
46
|
+
* Get info about currently running war in the clan.
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* await client.getCurrentWar('#8QU8J9LP');
|
|
50
|
+
* ```
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* await client.getCurrentWar({ clanTag: '#8QU8J9LP', round: 'PREVIOUS_ROUND' });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
getCurrentWar(clanTag: string | {
|
|
57
|
+
clanTag: string;
|
|
58
|
+
round?: keyof typeof CWL_ROUNDS;
|
|
59
|
+
}, options?: OverrideOptions): Promise<ClanWar | null>;
|
|
60
|
+
/**
|
|
61
|
+
* Get info about currently running CWL round.
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* await client.getLeagueWar('#8QU8J9LP');
|
|
65
|
+
* ```
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* await client.getLeagueWar({ clanTag: '#8QU8J9LP', round: 'PREVIOUS_ROUND' });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
getLeagueWar(clanTag: string | {
|
|
72
|
+
clanTag: string;
|
|
73
|
+
round?: keyof typeof CWL_ROUNDS;
|
|
74
|
+
}, options?: OverrideOptions): Promise<ClanWar | null>;
|
|
49
75
|
private _getCurrentLeagueWars;
|
|
50
76
|
private _getClanWars;
|
|
51
77
|
/** Get information about clan war league. */
|
|
@@ -54,7 +80,7 @@ export declare class Client extends EventEmitter {
|
|
|
54
80
|
getClanWarLeagueRound(warTag: string | {
|
|
55
81
|
warTag: string;
|
|
56
82
|
clanTag?: string;
|
|
57
|
-
}, options?: OverrideOptions): Promise<ClanWar
|
|
83
|
+
}, options?: OverrideOptions): Promise<ClanWar>;
|
|
58
84
|
/** Get information about a player by tag. */
|
|
59
85
|
getPlayer(playerTag: string, options?: OverrideOptions): Promise<Player>;
|
|
60
86
|
/** Verify Player API token that can be found from the Game settings. */
|
|
@@ -88,8 +114,8 @@ export declare class Client extends EventEmitter {
|
|
|
88
114
|
*
|
|
89
115
|
* **Parameters**
|
|
90
116
|
*
|
|
91
|
-
* | Name |
|
|
92
|
-
* | :--: |
|
|
117
|
+
* | Name | Type | Description |
|
|
118
|
+
* | :--: | :------: | :-------------------: |
|
|
93
119
|
* | `id` | `string` | Id of the new season. |
|
|
94
120
|
* @public
|
|
95
121
|
* @event
|
|
@@ -106,8 +132,8 @@ export declare class Client extends EventEmitter {
|
|
|
106
132
|
*
|
|
107
133
|
* **Parameters**
|
|
108
134
|
*
|
|
109
|
-
* |
|
|
110
|
-
* |
|
|
135
|
+
* | Name | Type | Description |
|
|
136
|
+
* | :--------: | :------: | :------------------------------------------------: |
|
|
111
137
|
* | `duration` | `number` | Duration of the maintenance break in milliseconds. |
|
|
112
138
|
* @public
|
|
113
139
|
* @event
|
|
@@ -135,13 +161,3 @@ export interface ClientEvents {
|
|
|
135
161
|
[EVENTS.WAR_LOOP_END]: [];
|
|
136
162
|
[EVENTS.ERROR]: [error: unknown];
|
|
137
163
|
}
|
|
138
|
-
export interface EventTypes {
|
|
139
|
-
CLAN: [oldClan: Clan, newClan: Clan];
|
|
140
|
-
PLAYER: [oldPlayer: Player, newPlayer: Player];
|
|
141
|
-
CLAN_WAR: [oldWar: ClanWar, newWar: ClanWar];
|
|
142
|
-
}
|
|
143
|
-
export declare const CWLRound: {
|
|
144
|
-
readonly PREVIOUS_WAR: "warEnded";
|
|
145
|
-
readonly CURRENT_WAR: "inWar";
|
|
146
|
-
readonly NEXT_WAR: "preparation";
|
|
147
|
-
};
|
package/dist/client/Client.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.Client = void 0;
|
|
4
4
|
const Constants_1 = require("../util/Constants");
|
|
5
|
+
const HTTPError_1 = require("../rest/HTTPError");
|
|
5
6
|
const RESTManager_1 = require("../rest/RESTManager");
|
|
6
7
|
const EventManager_1 = require("./EventManager");
|
|
7
|
-
const HTTPError_1 = require("../rest/HTTPError");
|
|
8
8
|
const events_1 = require("events");
|
|
9
9
|
const Util_1 = require("../util/Util");
|
|
10
10
|
const struct_1 = require("../struct");
|
|
@@ -62,30 +62,52 @@ class Client extends events_1.EventEmitter {
|
|
|
62
62
|
const { data } = await this.rest.getClanWarLog(clanTag, options);
|
|
63
63
|
return data.items.map((entry) => new struct_1.ClanWarLog(this, entry));
|
|
64
64
|
}
|
|
65
|
-
/** Get info about currently running war (
|
|
65
|
+
/** Get info about currently running war (normal or friendly) in the clan. */
|
|
66
66
|
async getClanWar(clanTag, options) {
|
|
67
|
-
const { data, maxAge } = await this.rest.getCurrentWar(clanTag, options);
|
|
68
|
-
if (data.state === 'notInWar')
|
|
69
|
-
|
|
67
|
+
const { data, maxAge, path, status } = await this.rest.getCurrentWar(clanTag, options);
|
|
68
|
+
if (data.state === 'notInWar') {
|
|
69
|
+
throw new HTTPError_1.HTTPError(HTTPError_1.notInWarError, status, path, maxAge);
|
|
70
|
+
}
|
|
70
71
|
return new struct_1.ClanWar(this, data, { clanTag, maxAge });
|
|
71
72
|
}
|
|
72
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* Get info about currently running war in the clan.
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* await client.getCurrentWar('#8QU8J9LP');
|
|
78
|
+
* ```
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* await client.getCurrentWar({ clanTag: '#8QU8J9LP', round: 'PREVIOUS_ROUND' });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
73
84
|
async getCurrentWar(clanTag, options) {
|
|
85
|
+
const args = typeof clanTag === 'string' ? { clanTag } : { clanTag: clanTag.clanTag, round: clanTag.round };
|
|
74
86
|
try {
|
|
75
|
-
|
|
76
|
-
return data ?? (await this.getLeagueWar(clanTag));
|
|
87
|
+
return await this.getClanWar(args.clanTag, options);
|
|
77
88
|
}
|
|
78
89
|
catch (e) {
|
|
79
90
|
if (e instanceof HTTPError_1.HTTPError && e.status === 403) {
|
|
80
|
-
return this.getLeagueWar(clanTag);
|
|
91
|
+
return this.getLeagueWar({ clanTag: args.clanTag, round: args.round });
|
|
81
92
|
}
|
|
82
93
|
}
|
|
83
94
|
return null;
|
|
84
95
|
}
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Get info about currently running CWL round.
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* await client.getLeagueWar('#8QU8J9LP');
|
|
101
|
+
* ```
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* await client.getLeagueWar({ clanTag: '#8QU8J9LP', round: 'PREVIOUS_ROUND' });
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
async getLeagueWar(clanTag, options) {
|
|
108
|
+
const args = typeof clanTag === 'string' ? { clanTag } : { clanTag: clanTag.clanTag, round: clanTag.round };
|
|
109
|
+
const state = (args.round && Constants_1.CWL_ROUNDS[args.round]) ?? 'inWar'; // eslint-disable-line
|
|
110
|
+
const data = await this.getClanWarLeagueGroup(args.clanTag, options);
|
|
89
111
|
const rounds = data.rounds.filter((round) => !round.warTags.includes('#0'));
|
|
90
112
|
if (!rounds.length)
|
|
91
113
|
return null;
|
|
@@ -95,8 +117,11 @@ class Client extends events_1.EventEmitter {
|
|
|
95
117
|
.map((round) => round.warTags)
|
|
96
118
|
.flat()
|
|
97
119
|
.reverse();
|
|
98
|
-
const wars = await this.util.allSettled(warTags.map((warTag) => this.getClanWarLeagueRound({ warTag, clanTag }, { ignoreRateLimit: true })));
|
|
99
|
-
|
|
120
|
+
const wars = await this.util.allSettled(warTags.map((warTag) => this.getClanWarLeagueRound({ warTag, clanTag: args.clanTag }, { ignoreRateLimit: true })));
|
|
121
|
+
if (args.round && args.round in Constants_1.CWL_ROUNDS) {
|
|
122
|
+
return wars.find((war) => war.state === state) ?? null;
|
|
123
|
+
}
|
|
124
|
+
return wars.find((war) => war.state === state) ?? wars.at(0) ?? null;
|
|
100
125
|
}
|
|
101
126
|
async _getCurrentLeagueWars(clanTag, options) {
|
|
102
127
|
const data = await this.getClanWarLeagueGroup(clanTag, options);
|
|
@@ -106,15 +131,12 @@ class Client extends events_1.EventEmitter {
|
|
|
106
131
|
async _getClanWars(clanTag, options) {
|
|
107
132
|
const date = new Date().getDate();
|
|
108
133
|
try {
|
|
109
|
-
|
|
110
|
-
if (!(date >= 1 && date <= 10))
|
|
111
|
-
return data ? [data] : [];
|
|
112
|
-
return data ? [data] : await this._getCurrentLeagueWars(clanTag);
|
|
134
|
+
return [await this.getClanWar(clanTag, options)];
|
|
113
135
|
}
|
|
114
136
|
catch (e) {
|
|
115
137
|
if (!(date >= 1 && date <= 10))
|
|
116
138
|
return [];
|
|
117
|
-
if (e instanceof HTTPError_1.HTTPError && e.status
|
|
139
|
+
if (e instanceof HTTPError_1.HTTPError && [200, 403].includes(e.status)) {
|
|
118
140
|
return this._getCurrentLeagueWars(clanTag);
|
|
119
141
|
}
|
|
120
142
|
return [];
|
|
@@ -128,9 +150,10 @@ class Client extends events_1.EventEmitter {
|
|
|
128
150
|
/** Get information about CWL round by WarTag. */
|
|
129
151
|
async getClanWarLeagueRound(warTag, options) {
|
|
130
152
|
const args = typeof warTag === 'string' ? { warTag } : { warTag: warTag.warTag, clanTag: warTag.clanTag };
|
|
131
|
-
const { data, maxAge } = await this.rest.getClanWarLeagueRound(args.warTag, options);
|
|
132
|
-
if (data.state === 'notInWar')
|
|
133
|
-
|
|
153
|
+
const { data, maxAge, status, path } = await this.rest.getClanWarLeagueRound(args.warTag, options);
|
|
154
|
+
if (data.state === 'notInWar') {
|
|
155
|
+
throw new HTTPError_1.HTTPError(HTTPError_1.notInWarError, status, path, maxAge);
|
|
156
|
+
}
|
|
134
157
|
return new struct_1.ClanWar(this, data, { warTag: args.warTag, clanTag: args.clanTag, maxAge });
|
|
135
158
|
}
|
|
136
159
|
/** Get information about a player by tag. */
|
|
@@ -206,8 +229,3 @@ class Client extends events_1.EventEmitter {
|
|
|
206
229
|
}
|
|
207
230
|
}
|
|
208
231
|
exports.Client = Client;
|
|
209
|
-
exports.CWLRound = {
|
|
210
|
-
PREVIOUS_WAR: 'warEnded',
|
|
211
|
-
CURRENT_WAR: 'inWar',
|
|
212
|
-
NEXT_WAR: 'preparation'
|
|
213
|
-
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Clan, ClanWar, Player } from '../struct';
|
|
2
|
+
import { Client } from './Client';
|
|
2
3
|
/** Represents Event Manager of the {@link Client} class. */
|
|
3
4
|
export declare class EventManager {
|
|
4
5
|
private readonly client;
|
|
@@ -13,25 +14,29 @@ export declare class EventManager {
|
|
|
13
14
|
private _maintenanceStartTime;
|
|
14
15
|
constructor(client: Client);
|
|
15
16
|
/** Initialize the Event Manager to start pulling. */
|
|
16
|
-
init(): Promise<
|
|
17
|
+
init(): Promise<string[]>;
|
|
18
|
+
/** Add a clan tag to clan events. */
|
|
17
19
|
addClans(...tags: string[]): this;
|
|
20
|
+
/** Delete a clan tag from clan events. */
|
|
18
21
|
deleteClans(...tags: string[]): this;
|
|
22
|
+
/** Add a player tag for player events. */
|
|
19
23
|
addPlayers(...tags: string[]): this;
|
|
24
|
+
/** Delete a player tag from player events. */
|
|
20
25
|
deletePlayers(...tags: string[]): this;
|
|
26
|
+
/** Add a clan tag for war events. */
|
|
21
27
|
addWars(...tags: string[]): this;
|
|
28
|
+
/** Delete a clan tag from war events. */
|
|
22
29
|
deleteWars(...tags: string[]): this;
|
|
23
30
|
/**
|
|
24
|
-
* Set your own custom event.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* @param event.filter - Filter of this event. Must return a boolean value.
|
|
31
|
+
* Set your own custom clan event.
|
|
32
|
+
*
|
|
33
|
+
* In order to emit the custom event, you must have this filter function that returns a boolean.
|
|
28
34
|
*
|
|
29
35
|
* @example
|
|
30
36
|
* ```js
|
|
31
|
-
* client.events.addClans(['#2PP', '']);
|
|
37
|
+
* client.events.addClans(['#2PP', '#8QU8J9LP']);
|
|
32
38
|
*
|
|
33
|
-
* client.events.
|
|
34
|
-
* type: 'CLAN',
|
|
39
|
+
* client.events.setClanEvent({
|
|
35
40
|
* name: 'clanMemberUpdate',
|
|
36
41
|
* filter: (oldClan, newClan) => {
|
|
37
42
|
* return oldClan.memberCount !== newClan.memberCount;
|
|
@@ -40,14 +45,35 @@ export declare class EventManager {
|
|
|
40
45
|
*
|
|
41
46
|
* client.on('clanMemberUpdate', (oldClan, newClan) => {
|
|
42
47
|
* console.log(oldClan.memberCount, newClan.memberCount);
|
|
43
|
-
* })
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* (async function () {
|
|
51
|
+
* await client.events.init();
|
|
52
|
+
* })();
|
|
44
53
|
* ```
|
|
45
54
|
* @returns
|
|
46
55
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
setClanEvent(event: {
|
|
57
|
+
name: string;
|
|
58
|
+
filter: (oldClan: Clan, newClan: Clan) => boolean;
|
|
59
|
+
}): this;
|
|
60
|
+
/**
|
|
61
|
+
* Set your own custom war event.
|
|
62
|
+
*
|
|
63
|
+
* In order to emit the custom event, you must have this filter function that returns a boolean.
|
|
64
|
+
*/
|
|
65
|
+
setWarEvent(event: {
|
|
66
|
+
name: string;
|
|
67
|
+
filter: (oldWar: ClanWar, newWar: ClanWar) => boolean;
|
|
68
|
+
}): this;
|
|
69
|
+
/**
|
|
70
|
+
* Set your own custom player event.
|
|
71
|
+
*
|
|
72
|
+
* In order to emit the custom event, you must have this filter function that returns a boolean.
|
|
73
|
+
*/
|
|
74
|
+
setPlayerEvent(event: {
|
|
49
75
|
name: string;
|
|
50
|
-
filter: (
|
|
76
|
+
filter: (oldPlayer: Player, newPlayer: Player) => boolean;
|
|
51
77
|
}): this;
|
|
52
78
|
private maintenanceHandler;
|
|
53
79
|
private seasonEndHandler;
|
|
@@ -31,36 +31,42 @@ class EventManager {
|
|
|
31
31
|
this.warUpdateHandler();
|
|
32
32
|
return Promise.resolve(this.client.eventNames());
|
|
33
33
|
}
|
|
34
|
+
/** Add a clan tag to clan events. */
|
|
34
35
|
addClans(...tags) {
|
|
35
36
|
for (const tag of tags) {
|
|
36
37
|
this._clanTags.add(this.client.util.parseTag(tag));
|
|
37
38
|
}
|
|
38
39
|
return this;
|
|
39
40
|
}
|
|
41
|
+
/** Delete a clan tag from clan events. */
|
|
40
42
|
deleteClans(...tags) {
|
|
41
43
|
for (const tag of tags) {
|
|
42
44
|
this._warTags.delete(this.client.util.parseTag(tag));
|
|
43
45
|
}
|
|
44
46
|
return this;
|
|
45
47
|
}
|
|
48
|
+
/** Add a player tag for player events. */
|
|
46
49
|
addPlayers(...tags) {
|
|
47
50
|
for (const tag of tags) {
|
|
48
51
|
this._playerTags.add(this.client.util.parseTag(tag));
|
|
49
52
|
}
|
|
50
53
|
return this;
|
|
51
54
|
}
|
|
55
|
+
/** Delete a player tag from player events. */
|
|
52
56
|
deletePlayers(...tags) {
|
|
53
57
|
for (const tag of tags) {
|
|
54
58
|
this._warTags.delete(this.client.util.parseTag(tag));
|
|
55
59
|
}
|
|
56
60
|
return this;
|
|
57
61
|
}
|
|
62
|
+
/** Add a clan tag for war events. */
|
|
58
63
|
addWars(...tags) {
|
|
59
64
|
for (const tag of tags) {
|
|
60
65
|
this._warTags.add(this.client.util.parseTag(tag));
|
|
61
66
|
}
|
|
62
67
|
return this;
|
|
63
68
|
}
|
|
69
|
+
/** Delete a clan tag from war events. */
|
|
64
70
|
deleteWars(...tags) {
|
|
65
71
|
for (const tag of tags) {
|
|
66
72
|
this._warTags.delete(this.client.util.parseTag(tag));
|
|
@@ -68,17 +74,15 @@ class EventManager {
|
|
|
68
74
|
return this;
|
|
69
75
|
}
|
|
70
76
|
/**
|
|
71
|
-
* Set your own custom event.
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @param event.filter - Filter of this event. Must return a boolean value.
|
|
77
|
+
* Set your own custom clan event.
|
|
78
|
+
*
|
|
79
|
+
* In order to emit the custom event, you must have this filter function that returns a boolean.
|
|
75
80
|
*
|
|
76
81
|
* @example
|
|
77
82
|
* ```js
|
|
78
|
-
* client.events.addClans(['#2PP', '']);
|
|
83
|
+
* client.events.addClans(['#2PP', '#8QU8J9LP']);
|
|
79
84
|
*
|
|
80
|
-
* client.events.
|
|
81
|
-
* type: 'CLAN',
|
|
85
|
+
* client.events.setClanEvent({
|
|
82
86
|
* name: 'clanMemberUpdate',
|
|
83
87
|
* filter: (oldClan, newClan) => {
|
|
84
88
|
* return oldClan.memberCount !== newClan.memberCount;
|
|
@@ -87,27 +91,34 @@ class EventManager {
|
|
|
87
91
|
*
|
|
88
92
|
* client.on('clanMemberUpdate', (oldClan, newClan) => {
|
|
89
93
|
* console.log(oldClan.memberCount, newClan.memberCount);
|
|
90
|
-
* })
|
|
94
|
+
* });
|
|
95
|
+
*
|
|
96
|
+
* (async function () {
|
|
97
|
+
* await client.events.init();
|
|
98
|
+
* })();
|
|
91
99
|
* ```
|
|
92
100
|
* @returns
|
|
93
101
|
*/
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
setClanEvent(event) {
|
|
103
|
+
this._events.clans.push({ name: event.name, fn: event.filter });
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Set your own custom war event.
|
|
108
|
+
*
|
|
109
|
+
* In order to emit the custom event, you must have this filter function that returns a boolean.
|
|
110
|
+
*/
|
|
111
|
+
setWarEvent(event) {
|
|
112
|
+
this._events.wars.push({ name: event.name, fn: event.filter });
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Set your own custom player event.
|
|
117
|
+
*
|
|
118
|
+
* In order to emit the custom event, you must have this filter function that returns a boolean.
|
|
119
|
+
*/
|
|
120
|
+
setPlayerEvent(event) {
|
|
121
|
+
this._events.players.push({ name: event.name, fn: event.filter });
|
|
111
122
|
return this;
|
|
112
123
|
}
|
|
113
124
|
async maintenanceHandler() {
|
package/dist/rest/HTTPError.d.ts
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Represents an HTTP Error.
|
|
3
|
+
*/
|
|
2
4
|
export declare class HTTPError extends Error {
|
|
5
|
+
/** The message of this error. */
|
|
6
|
+
message: string;
|
|
7
|
+
/** The HTTP method of this request. */
|
|
3
8
|
method: string;
|
|
9
|
+
/** The reason of this error. */
|
|
4
10
|
reason: string;
|
|
11
|
+
/** The HTTP status code of this request. */
|
|
5
12
|
status: number;
|
|
13
|
+
/** The path of this request. */
|
|
6
14
|
path: string;
|
|
7
|
-
|
|
15
|
+
/** Maximum number of milliseconds the results can be cached. */
|
|
16
|
+
maxAge: number;
|
|
17
|
+
constructor(error: any, status: number, path: string, maxAge: number, method?: string);
|
|
8
18
|
}
|
|
19
|
+
export declare const notInWarError: {
|
|
20
|
+
message: string;
|
|
21
|
+
reason: string;
|
|
22
|
+
};
|
|
23
|
+
export declare const privateWarLogError: {
|
|
24
|
+
message: string;
|
|
25
|
+
reason: string;
|
|
26
|
+
};
|
package/dist/rest/HTTPError.js
CHANGED
|
@@ -1,23 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HTTPError = void 0;
|
|
3
|
+
exports.privateWarLogError = exports.notInWarError = exports.HTTPError = void 0;
|
|
4
4
|
const messages = {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
500: 'Unknown error happened when handling the request.',
|
|
6
|
+
504: 'The user aborted this request.',
|
|
7
|
+
404: 'Requested resource was not found.',
|
|
8
|
+
400: 'Client provided incorrect parameters for the request.',
|
|
9
|
+
503: 'Service is temporarily unavailable because of maintenance.',
|
|
10
|
+
429: 'Request was throttled, because amount of requests was above the threshold defined for the used API token.',
|
|
11
|
+
403: 'Access denied, either because of missing/incorrect credentials or used API token does not grant access to the requested resource.'
|
|
7
12
|
};
|
|
8
13
|
const reasons = {
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
503: 'serviceUnavailable',
|
|
15
|
+
429: 'tooManyRequests',
|
|
16
|
+
400: 'badRequest',
|
|
17
|
+
403: 'forbidden',
|
|
18
|
+
500: 'unknownError',
|
|
19
|
+
404: 'notFound',
|
|
20
|
+
504: 'requestAborted'
|
|
11
21
|
};
|
|
12
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Represents an HTTP Error.
|
|
24
|
+
*/
|
|
13
25
|
class HTTPError extends Error {
|
|
14
|
-
constructor(error, status, path, method
|
|
26
|
+
constructor(error, status, path, maxAge, method) {
|
|
15
27
|
super();
|
|
16
28
|
this.message = error?.message ?? messages[status];
|
|
17
29
|
this.reason = error?.reason ?? reasons[status];
|
|
18
30
|
this.path = path;
|
|
19
|
-
this.method = method;
|
|
31
|
+
this.method = method ?? 'GET';
|
|
20
32
|
this.status = status;
|
|
33
|
+
this.maxAge = maxAge;
|
|
21
34
|
}
|
|
22
35
|
}
|
|
23
36
|
exports.HTTPError = HTTPError;
|
|
37
|
+
exports.notInWarError = {
|
|
38
|
+
message: 'Clan is not in war at this moment.',
|
|
39
|
+
reason: 'notInWar'
|
|
40
|
+
};
|
|
41
|
+
exports.privateWarLogError = {
|
|
42
|
+
message: 'Access denied, clan war log is private.',
|
|
43
|
+
reason: 'privateWarLog'
|
|
44
|
+
};
|
|
@@ -8,120 +8,144 @@ export declare class RESTManager {
|
|
|
8
8
|
data: APIClanList;
|
|
9
9
|
maxAge: number;
|
|
10
10
|
status: number;
|
|
11
|
+
path: string;
|
|
11
12
|
}>;
|
|
12
13
|
getClan(clanTag: string, options?: OverrideOptions): Promise<{
|
|
13
14
|
data: APIClan;
|
|
14
15
|
maxAge: number;
|
|
15
16
|
status: number;
|
|
17
|
+
path: string;
|
|
16
18
|
}>;
|
|
17
19
|
getClanMembers(clanTag: string, options?: SearchOptions): Promise<{
|
|
18
20
|
data: APIClanMemberList;
|
|
19
21
|
maxAge: number;
|
|
20
22
|
status: number;
|
|
23
|
+
path: string;
|
|
21
24
|
}>;
|
|
22
25
|
getClanWarLog(clanTag: string, options?: SearchOptions): Promise<{
|
|
23
26
|
data: APIClanWarLog;
|
|
24
27
|
maxAge: number;
|
|
25
28
|
status: number;
|
|
29
|
+
path: string;
|
|
26
30
|
}>;
|
|
27
31
|
getCurrentWar(clanTag: string, options?: OverrideOptions): Promise<{
|
|
28
32
|
data: APIClanWar;
|
|
29
33
|
maxAge: number;
|
|
30
34
|
status: number;
|
|
35
|
+
path: string;
|
|
31
36
|
}>;
|
|
32
37
|
getClanWarLeagueGroup(clanTag: string, options?: OverrideOptions): Promise<{
|
|
33
38
|
data: APIClanWarLeagueGroup;
|
|
34
39
|
maxAge: number;
|
|
35
40
|
status: number;
|
|
41
|
+
path: string;
|
|
36
42
|
}>;
|
|
37
43
|
getClanWarLeagueRound(warTag: string, options?: OverrideOptions): Promise<{
|
|
38
44
|
data: APIClanWar;
|
|
39
45
|
maxAge: number;
|
|
40
46
|
status: number;
|
|
47
|
+
path: string;
|
|
41
48
|
}>;
|
|
42
49
|
getPlayer(playerTag: string, options?: OverrideOptions): Promise<{
|
|
43
50
|
data: APIPlayer;
|
|
44
51
|
maxAge: number;
|
|
45
52
|
status: number;
|
|
53
|
+
path: string;
|
|
46
54
|
}>;
|
|
47
55
|
postPlayerToken(playerTag: string, token: string, options?: OverrideOptions): Promise<{
|
|
48
56
|
data: APIVerifyToken;
|
|
49
57
|
maxAge: number;
|
|
50
58
|
status: number;
|
|
59
|
+
path: string;
|
|
51
60
|
}>;
|
|
52
61
|
getLeagues(options?: SearchOptions): Promise<{
|
|
53
62
|
data: APILeagueList;
|
|
54
63
|
maxAge: number;
|
|
55
64
|
status: number;
|
|
65
|
+
path: string;
|
|
56
66
|
}>;
|
|
57
67
|
getLeague(leagueId: string | number, options?: OverrideOptions): Promise<{
|
|
58
68
|
data: APILeague;
|
|
59
69
|
maxAge: number;
|
|
60
70
|
status: number;
|
|
71
|
+
path: string;
|
|
61
72
|
}>;
|
|
62
73
|
getLeagueSeasons(leagueId: number, options?: SearchOptions): Promise<{
|
|
63
74
|
data: APILeagueSeasonList;
|
|
64
75
|
maxAge: number;
|
|
65
76
|
status: number;
|
|
77
|
+
path: string;
|
|
66
78
|
}>;
|
|
67
79
|
getSeasonRankings(leagueId: number, seasonId: string, options?: SearchOptions): Promise<{
|
|
68
80
|
data: APIPlayerSeasonRankingList;
|
|
69
81
|
maxAge: number;
|
|
70
82
|
status: number;
|
|
83
|
+
path: string;
|
|
71
84
|
}>;
|
|
72
85
|
getWarLeagues(options?: SearchOptions): Promise<{
|
|
73
86
|
data: APIWarLeagueList;
|
|
74
87
|
maxAge: number;
|
|
75
88
|
status: number;
|
|
89
|
+
path: string;
|
|
76
90
|
}>;
|
|
77
91
|
getWarLeague(leagueId: number, options?: OverrideOptions): Promise<{
|
|
78
92
|
data: APIWarLeague;
|
|
79
93
|
maxAge: number;
|
|
80
94
|
status: number;
|
|
95
|
+
path: string;
|
|
81
96
|
}>;
|
|
82
97
|
getLocations(options?: SearchOptions): Promise<{
|
|
83
98
|
data: APILocationList;
|
|
84
99
|
maxAge: number;
|
|
85
100
|
status: number;
|
|
101
|
+
path: string;
|
|
86
102
|
}>;
|
|
87
103
|
getLocation(locationId: number, options?: OverrideOptions): Promise<{
|
|
88
104
|
data: APILocation;
|
|
89
105
|
maxAge: number;
|
|
90
106
|
status: number;
|
|
107
|
+
path: string;
|
|
91
108
|
}>;
|
|
92
109
|
getClanRanks(locationId: number | string, options?: SearchOptions): Promise<{
|
|
93
110
|
data: APIClanRankingList;
|
|
94
111
|
maxAge: number;
|
|
95
112
|
status: number;
|
|
113
|
+
path: string;
|
|
96
114
|
}>;
|
|
97
115
|
getPlayerRanks(locationId: number | string, options?: SearchOptions): Promise<{
|
|
98
116
|
data: APIPlayerRankingList;
|
|
99
117
|
maxAge: number;
|
|
100
118
|
status: number;
|
|
119
|
+
path: string;
|
|
101
120
|
}>;
|
|
102
121
|
getVersusClanRanks(locationId: number | string, options?: SearchOptions): Promise<{
|
|
103
122
|
data: APIClanVersusRankingList;
|
|
104
123
|
maxAge: number;
|
|
105
124
|
status: number;
|
|
125
|
+
path: string;
|
|
106
126
|
}>;
|
|
107
127
|
getVersusPlayerRanks(locationId: number | string, options?: SearchOptions): Promise<{
|
|
108
128
|
data: APIPlayerVersusRankingList;
|
|
109
129
|
maxAge: number;
|
|
110
130
|
status: number;
|
|
131
|
+
path: string;
|
|
111
132
|
}>;
|
|
112
133
|
getClanLabels(options?: SearchOptions): Promise<{
|
|
113
134
|
data: APILabelList;
|
|
114
135
|
maxAge: number;
|
|
115
136
|
status: number;
|
|
137
|
+
path: string;
|
|
116
138
|
}>;
|
|
117
139
|
getPlayerLabels(options?: SearchOptions): Promise<{
|
|
118
140
|
data: APILabelList;
|
|
119
141
|
maxAge: number;
|
|
120
142
|
status: number;
|
|
143
|
+
path: string;
|
|
121
144
|
}>;
|
|
122
145
|
getGoldPassSeason(options?: OverrideOptions): Promise<{
|
|
123
146
|
data: APIGoldPassSeason;
|
|
124
147
|
maxAge: number;
|
|
125
148
|
status: number;
|
|
149
|
+
path: string;
|
|
126
150
|
}>;
|
|
127
151
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { QueueThrottler, BatchThrottler } from './Throttler';
|
|
2
|
+
import Keyv from 'keyv';
|
|
2
3
|
/** Represents a Request Handler. */
|
|
3
4
|
export declare class RequestHandler {
|
|
4
5
|
#private;
|
|
@@ -6,7 +7,7 @@ export declare class RequestHandler {
|
|
|
6
7
|
private password;
|
|
7
8
|
private keyCount;
|
|
8
9
|
private keyName;
|
|
9
|
-
private keyDescription
|
|
10
|
+
private keyDescription?;
|
|
10
11
|
private keys;
|
|
11
12
|
private readonly baseURL;
|
|
12
13
|
private readonly retryLimit;
|
|
@@ -21,54 +22,134 @@ export declare class RequestHandler {
|
|
|
21
22
|
data: T;
|
|
22
23
|
maxAge: number;
|
|
23
24
|
status: number;
|
|
25
|
+
path: string;
|
|
24
26
|
}>;
|
|
25
27
|
private exec;
|
|
26
28
|
init(options: InitOptions): Promise<string[]>;
|
|
29
|
+
private reValidateKeys;
|
|
27
30
|
private login;
|
|
28
31
|
private getKeys;
|
|
29
32
|
private revokeKey;
|
|
30
33
|
private createKey;
|
|
31
34
|
private getIp;
|
|
32
35
|
}
|
|
36
|
+
/** Options for a client. */
|
|
33
37
|
export interface ClientOptions {
|
|
38
|
+
/** Keys from Clash of Clans API developer site. */
|
|
34
39
|
keys?: string[];
|
|
35
|
-
|
|
40
|
+
/** Base URL of the Clash of Clans API. */
|
|
36
41
|
baseURL?: string;
|
|
42
|
+
/**
|
|
43
|
+
* How many times to retry on 5XX errors.
|
|
44
|
+
*/
|
|
37
45
|
retryLimit?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Whether enable or disable internal caching.
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* const client = new Client({ cache: true });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
cache?: boolean | Keyv;
|
|
54
|
+
/** Time to wait before cancelling a REST request, in milliseconds. */
|
|
38
55
|
restRequestTimeout?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Throttler class which handles rate-limit
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const client = new Client({ throttler: new QueueThrottler(1000 / 10) });
|
|
61
|
+
* ```
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const client = new Client({ throttler: new BatchThrottler(30) });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
39
67
|
throttler?: QueueThrottler | BatchThrottler;
|
|
40
68
|
}
|
|
69
|
+
/** Search options for request. */
|
|
41
70
|
export interface SearchOptions extends OverrideOptions {
|
|
71
|
+
/** Limit the number of items returned in the response. */
|
|
42
72
|
limit?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Return only items that occur after this marker.
|
|
75
|
+
* Before marker can be found from the response, inside the 'paging' property.
|
|
76
|
+
* Note that only after or before can be specified for a request, not both.
|
|
77
|
+
*/
|
|
43
78
|
after?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Return only items that occur before this marker.
|
|
81
|
+
* Before marker can be found from the response, inside the 'paging' property.
|
|
82
|
+
* Note that only after or before can be specified for a request, not both.
|
|
83
|
+
*/
|
|
44
84
|
before?: string;
|
|
45
85
|
}
|
|
86
|
+
/** Override options for a request. */
|
|
46
87
|
export interface OverrideOptions {
|
|
88
|
+
/** Whether to cache this response. */
|
|
89
|
+
cache?: boolean;
|
|
90
|
+
/** Whether to skip the cache check and request the API. */
|
|
91
|
+
force?: boolean;
|
|
92
|
+
/** How many times to retry on 5XX errors. */
|
|
47
93
|
retryLimit?: string;
|
|
94
|
+
/** Whether to ignore throttlers. */
|
|
48
95
|
ignoreRateLimit?: boolean;
|
|
96
|
+
/** Time to wait before cancelling a REST request, in milliseconds. */
|
|
49
97
|
restRequestTimeout?: number;
|
|
50
98
|
}
|
|
51
99
|
export interface RequestOptions extends OverrideOptions {
|
|
52
100
|
body?: string;
|
|
53
101
|
method?: string;
|
|
54
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Clan search options for a request.
|
|
105
|
+
*
|
|
106
|
+
* ::info
|
|
107
|
+
* If name is used as part of search query, it needs to be at least three characters long.
|
|
108
|
+
* Name search parameter is interpreted as wild card search, so it may appear anywhere in the clan name.
|
|
109
|
+
* :::
|
|
110
|
+
*/
|
|
55
111
|
export interface ClanSearchOptions {
|
|
112
|
+
/** Search clans by name. */
|
|
56
113
|
name?: string;
|
|
114
|
+
/** Filter by minimum number of clan members. */
|
|
57
115
|
minMembers?: number;
|
|
116
|
+
/** Filter by maximum number of clan members. */
|
|
58
117
|
maxMembers?: number;
|
|
118
|
+
/** Filter by minimum amount of clan points. */
|
|
59
119
|
minClanPoints?: number;
|
|
120
|
+
/** Filter by minimum clan level. */
|
|
60
121
|
minClanLevel?: number;
|
|
122
|
+
/** Filter by clan war frequency. */
|
|
61
123
|
warFrequency?: string;
|
|
124
|
+
/** Filter by clan location identifier. For list of available locations, refer to getLocations operation. */
|
|
62
125
|
locationId?: string;
|
|
126
|
+
/** Comma separated list of label IDs to use for filtering results. */
|
|
63
127
|
labelIds?: string;
|
|
128
|
+
/** Limit the number of items returned in the response. */
|
|
64
129
|
limit?: number;
|
|
130
|
+
/**
|
|
131
|
+
* Return only items that occur after this marker.
|
|
132
|
+
* Before marker can be found from the response, inside the 'paging' property.
|
|
133
|
+
* Note that only after or before can be specified for a request, not both.
|
|
134
|
+
*/
|
|
65
135
|
after?: string;
|
|
136
|
+
/**
|
|
137
|
+
* Return only items that occur before this marker.
|
|
138
|
+
* Before marker can be found from the response, inside the 'paging' property.
|
|
139
|
+
* Note that only after or before can be specified for a request, not both.
|
|
140
|
+
*/
|
|
66
141
|
before?: string;
|
|
67
142
|
}
|
|
143
|
+
/** Login options for a client. */
|
|
68
144
|
export interface InitOptions {
|
|
145
|
+
/** Developer site email address. */
|
|
69
146
|
email: string;
|
|
147
|
+
/** Developer site password. */
|
|
70
148
|
password: string;
|
|
149
|
+
/** Name of API key(s). */
|
|
71
150
|
keyName?: string;
|
|
151
|
+
/** Number of allowed API keys. */
|
|
72
152
|
keyCount?: number;
|
|
153
|
+
/** Description of API key(s). */
|
|
73
154
|
keyDescription?: string;
|
|
74
155
|
}
|
|
@@ -30,8 +30,11 @@ class RequestHandler {
|
|
|
30
30
|
this.retryLimit = options?.retryLimit ?? 0;
|
|
31
31
|
this.throttler = options?.throttler ?? null;
|
|
32
32
|
this.baseURL = options?.baseURL ?? Constants_1.API_BASE_URL;
|
|
33
|
-
this.cached = options?.cache ? new keyv_1.default() : null;
|
|
34
33
|
this.restRequestTimeout = options?.restRequestTimeout ?? 0;
|
|
34
|
+
if (options?.cache instanceof keyv_1.default)
|
|
35
|
+
this.cached = options.cache;
|
|
36
|
+
else
|
|
37
|
+
this.cached = options?.cache ? new keyv_1.default() : null;
|
|
35
38
|
}
|
|
36
39
|
get _keys() {
|
|
37
40
|
return Array.isArray(this.keys) ? this.keys : [this.keys];
|
|
@@ -47,8 +50,8 @@ class RequestHandler {
|
|
|
47
50
|
}
|
|
48
51
|
async request(path, options = {}) {
|
|
49
52
|
const cached = (await this.cached?.get(path)) ?? null;
|
|
50
|
-
if (cached)
|
|
51
|
-
return { data: cached, maxAge: 0, status: 200 };
|
|
53
|
+
if (cached && options.force !== true)
|
|
54
|
+
return { data: cached, maxAge: 0, status: 200, path };
|
|
52
55
|
if (!this.throttler || options.ignoreRateLimit)
|
|
53
56
|
return this.exec(path, options);
|
|
54
57
|
await this.throttler.wait();
|
|
@@ -74,23 +77,40 @@ class RequestHandler {
|
|
|
74
77
|
await this.login();
|
|
75
78
|
return this.exec(path, options, ++retries);
|
|
76
79
|
}
|
|
80
|
+
const maxAge = Number(res?.headers.get('cache-control')?.split('=')?.[1] ?? 0) * 1000;
|
|
81
|
+
if (res?.status === 403 && !data?.message)
|
|
82
|
+
throw new HTTPError_1.HTTPError(HTTPError_1.privateWarLogError, res.status, path, maxAge);
|
|
77
83
|
if (!res?.ok)
|
|
78
|
-
throw new HTTPError_1.HTTPError(data, res?.status ?? 504, path, options.method);
|
|
79
|
-
|
|
80
|
-
if (this.cached && maxAge > 0)
|
|
84
|
+
throw new HTTPError_1.HTTPError(data, res?.status ?? 504, path, maxAge, options.method);
|
|
85
|
+
if (this.cached && maxAge > 0 && options.cache !== false)
|
|
81
86
|
await this.cached.set(path, data, maxAge);
|
|
82
|
-
return { data, maxAge, status: res.status };
|
|
87
|
+
return { data, maxAge, status: res.status, path };
|
|
83
88
|
}
|
|
84
|
-
init(options) {
|
|
89
|
+
async init(options) {
|
|
85
90
|
if (!(options.email && options.password))
|
|
86
91
|
throw ReferenceError('Missing email and password.');
|
|
87
|
-
this.keyDescription = options.keyDescription
|
|
92
|
+
this.keyDescription = options.keyDescription;
|
|
88
93
|
this.keyName = options.keyName ?? 'clashofclans.js.keys';
|
|
89
94
|
this.keyCount = Math.min(options.keyCount ?? 1, 10);
|
|
90
95
|
this.password = options.password;
|
|
91
96
|
this.email = options.email;
|
|
97
|
+
await this.reValidateKeys();
|
|
92
98
|
return this.login();
|
|
93
99
|
}
|
|
100
|
+
async reValidateKeys() {
|
|
101
|
+
for (const key of this.keys) {
|
|
102
|
+
const res = await (0, node_fetch_1.default)(`${this.baseURL}/locations?limit=1`, {
|
|
103
|
+
method: 'GET',
|
|
104
|
+
timeout: 10000,
|
|
105
|
+
headers: { 'Authorization': `Bearer ${key}`, 'Content-Type': 'application/json' }
|
|
106
|
+
}).catch(() => null);
|
|
107
|
+
if (res?.status === 403) {
|
|
108
|
+
const index = this.keys.indexOf(key);
|
|
109
|
+
this.keys.splice(index, 1);
|
|
110
|
+
console.warn(`[WARN] Pre-defined key #${index + 1} is no longer valid. Removed from the key list.`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
94
114
|
async login() {
|
|
95
115
|
const res = await (0, node_fetch_1.default)(`${Constants_1.DEV_SITE_API_BASE_URL}/login`, {
|
|
96
116
|
method: 'POST',
|
|
@@ -120,9 +140,12 @@ class RequestHandler {
|
|
|
120
140
|
keys.splice(index, 1);
|
|
121
141
|
}
|
|
122
142
|
// Filter keys for current IP address and specified key name.
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
143
|
+
for (const key of keys.filter((key) => key.name === this.keyName && key.cidrRanges.includes(ip))) {
|
|
144
|
+
if (this.keys.length >= this.keyCount)
|
|
145
|
+
break;
|
|
146
|
+
if (!this.keys.includes(key.key))
|
|
147
|
+
this.keys.push(key.key);
|
|
148
|
+
}
|
|
126
149
|
// Create keys within limits (maximum of 10 keys per account)
|
|
127
150
|
while (this.keys.length < this.keyCount && keys.length < 10) {
|
|
128
151
|
const key = await this.createKey(cookie, ip);
|
|
@@ -152,7 +175,11 @@ class RequestHandler {
|
|
|
152
175
|
const res = await (0, node_fetch_1.default)(`${Constants_1.DEV_SITE_API_BASE_URL}/apikey/create`, {
|
|
153
176
|
method: 'POST',
|
|
154
177
|
headers: { 'Content-Type': 'application/json', cookie },
|
|
155
|
-
body: JSON.stringify({
|
|
178
|
+
body: JSON.stringify({
|
|
179
|
+
cidrRanges: [ip],
|
|
180
|
+
name: this.keyName,
|
|
181
|
+
description: this.keyDescription ?? new Date().toUTCString()
|
|
182
|
+
})
|
|
156
183
|
});
|
|
157
184
|
const data = await res.json();
|
|
158
185
|
return data.key;
|
package/dist/struct/ClanWar.d.ts
CHANGED
|
@@ -116,12 +116,12 @@ export declare class ClanWar {
|
|
|
116
116
|
opponent: WarClan;
|
|
117
117
|
/** The war's unique tag. This is `null` unless this is a CWL. */
|
|
118
118
|
warTag: string | null;
|
|
119
|
-
/**
|
|
119
|
+
/** Maximum number of milliseconds the results can be cached. */
|
|
120
120
|
maxAge: number;
|
|
121
121
|
constructor(client: Client, data: APIClanWar, extra: {
|
|
122
122
|
clanTag?: string;
|
|
123
123
|
warTag?: string;
|
|
124
|
-
maxAge
|
|
124
|
+
maxAge: number;
|
|
125
125
|
});
|
|
126
126
|
/** Return a {@link ClanWarMember} with the tag provided. */
|
|
127
127
|
getMember(tag: string): ClanWarMember | null;
|
|
@@ -129,7 +129,9 @@ export declare class ClanWar {
|
|
|
129
129
|
getAttack(attackerTag: string, defenderTag: string): ClanWarAttack | null;
|
|
130
130
|
/** Return a list of {@link ClanWarAttack} for the defenderTag provided. */
|
|
131
131
|
getDefenses(defenderTag: string): ClanWarAttack[];
|
|
132
|
-
/** Returns either `friendly`, `cwl` or `
|
|
133
|
-
get type():
|
|
132
|
+
/** Returns either `friendly`, `cwl` or `normal`. */
|
|
133
|
+
get type(): "friendly" | "cwl" | "normal";
|
|
134
134
|
private get _isFriendly();
|
|
135
|
+
/** Returns the war status, based off the home clan. */
|
|
136
|
+
get status(): "win" | "lose" | "tie" | "pending";
|
|
135
137
|
}
|
package/dist/struct/ClanWar.js
CHANGED
|
@@ -147,7 +147,7 @@ class ClanWar {
|
|
|
147
147
|
}
|
|
148
148
|
this.clan = new WarClan(this, clan);
|
|
149
149
|
this.opponent = new WarClan(this, opponent);
|
|
150
|
-
this.maxAge =
|
|
150
|
+
this.maxAge = extra.maxAge;
|
|
151
151
|
}
|
|
152
152
|
/** Return a {@link ClanWarMember} with the tag provided. */
|
|
153
153
|
getMember(tag) {
|
|
@@ -168,17 +168,31 @@ class ClanWar {
|
|
|
168
168
|
}
|
|
169
169
|
return this.opponent.attacks.filter((atk) => atk.defenderTag === defenderTag);
|
|
170
170
|
}
|
|
171
|
-
/** Returns either `friendly`, `cwl` or `
|
|
171
|
+
/** Returns either `friendly`, `cwl` or `normal`. */
|
|
172
172
|
get type() {
|
|
173
173
|
if (this._isFriendly)
|
|
174
174
|
return 'friendly';
|
|
175
175
|
if (this.warTag)
|
|
176
176
|
return 'cwl';
|
|
177
|
-
return '
|
|
177
|
+
return 'normal';
|
|
178
178
|
}
|
|
179
179
|
get _isFriendly() {
|
|
180
180
|
const preparationTime = this.startTime.getTime() - this.preparationStartTime.getTime();
|
|
181
181
|
return Constants_1.FRIENDLY_WAR_PREPARATION_TIMES.includes(preparationTime);
|
|
182
182
|
}
|
|
183
|
+
/** Returns the war status, based off the home clan. */
|
|
184
|
+
get status() {
|
|
185
|
+
if (this.state === 'preparation')
|
|
186
|
+
return 'pending';
|
|
187
|
+
if (this.clan.stars > this.opponent.stars)
|
|
188
|
+
return 'win';
|
|
189
|
+
if (this.clan.stars === this.opponent.stars) {
|
|
190
|
+
if (this.clan.destruction > this.opponent.destruction)
|
|
191
|
+
return 'win';
|
|
192
|
+
if (this.clan.destruction === this.opponent.destruction)
|
|
193
|
+
return 'tie';
|
|
194
|
+
}
|
|
195
|
+
return 'lose';
|
|
196
|
+
}
|
|
183
197
|
}
|
|
184
198
|
exports.ClanWar = ClanWar;
|
|
@@ -57,7 +57,7 @@ class ClanWarLeagueGroup {
|
|
|
57
57
|
const warTags = rounds.map((round) => round.warTags).flat();
|
|
58
58
|
const wars = await Promise.allSettled(warTags.map((warTag) => this.client.getClanWarLeagueRound({ warTag, clanTag }, { ignoreRateLimit: true })));
|
|
59
59
|
return wars
|
|
60
|
-
.filter((res) => res.status === 'fulfilled'
|
|
60
|
+
.filter((res) => res.status === 'fulfilled')
|
|
61
61
|
.map((res) => res.value)
|
|
62
62
|
.filter((war) => (clanTag ? war.clan.tag === clanTag : true));
|
|
63
63
|
}
|
|
@@ -71,7 +71,7 @@ class ClanWarLeagueGroup {
|
|
|
71
71
|
.flat();
|
|
72
72
|
const wars = await Promise.allSettled(warTags.map((warTag) => this.client.getClanWarLeagueRound({ warTag, clanTag }, { ignoreRateLimit: true })));
|
|
73
73
|
return wars
|
|
74
|
-
.filter((res) => res.status === 'fulfilled'
|
|
74
|
+
.filter((res) => res.status === 'fulfilled')
|
|
75
75
|
.map((res) => res.value)
|
|
76
76
|
.filter((war) => war.clan.tag === clanTag);
|
|
77
77
|
}
|
|
@@ -49,6 +49,6 @@ export declare class ClanWarLog {
|
|
|
49
49
|
/** The opposition clan. */
|
|
50
50
|
opponent: WarLogClan;
|
|
51
51
|
constructor(client: Client, data: APIClanWarLogEntry);
|
|
52
|
-
/** Returns either `friendly`, `cwl` or `
|
|
53
|
-
get type(): "friendly" | "cwl" | "
|
|
52
|
+
/** Returns either `friendly`, `cwl` or `normal`. */
|
|
53
|
+
get type(): "friendly" | "cwl" | "normal";
|
|
54
54
|
}
|
|
@@ -34,13 +34,13 @@ class ClanWarLog {
|
|
|
34
34
|
this.clan = new WarLogClan(data.clan);
|
|
35
35
|
this.opponent = new WarLogClan(data.opponent);
|
|
36
36
|
}
|
|
37
|
-
/** Returns either `friendly`, `cwl` or `
|
|
37
|
+
/** Returns either `friendly`, `cwl` or `normal`. */
|
|
38
38
|
get type() {
|
|
39
39
|
if (!this.clan.expEarned)
|
|
40
40
|
return 'friendly';
|
|
41
41
|
if (!this.opponent.tag)
|
|
42
42
|
return 'cwl';
|
|
43
|
-
return '
|
|
43
|
+
return 'normal';
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
exports.ClanWarLog = ClanWarLog;
|
package/dist/types/index.d.ts
CHANGED
package/dist/util/Constants.d.ts
CHANGED
|
@@ -34,3 +34,8 @@ export declare const EVENTS: {
|
|
|
34
34
|
readonly MAINTENANCE_END: "maintenanceEnd";
|
|
35
35
|
readonly ERROR: "error";
|
|
36
36
|
};
|
|
37
|
+
export declare const CWL_ROUNDS: {
|
|
38
|
+
readonly PREVIOUS_ROUND: "warEnded";
|
|
39
|
+
readonly CURRENT_ROUND: "inWar";
|
|
40
|
+
readonly NEXT_ROUND: "preparation";
|
|
41
|
+
};
|
package/dist/util/Constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EVENTS = exports.FRIENDLY_WAR_PREPARATION_TIMES = exports.WAR_LEAGUES = exports.LEAGUES = exports.LEGEND_LEAGUE_ID = exports.UNRANKED_LEAGUE_DATA = exports.HERO_PETS = exports.HEROES = exports.SPELLS = exports.DARK_ELIXIR_SPELLS = exports.ELIXIR_SPELLS = exports.SUPER_TROOPS = exports.SIEGE_MACHINES = exports.HOME_TROOPS = exports.DARK_ELIXIR_TROOPS = exports.ELIXIR_TROOPS = exports.DEV_SITE_API_BASE_URL = exports.API_BASE_URL = void 0;
|
|
3
|
+
exports.CWL_ROUNDS = exports.EVENTS = exports.FRIENDLY_WAR_PREPARATION_TIMES = exports.WAR_LEAGUES = exports.LEAGUES = exports.LEGEND_LEAGUE_ID = exports.UNRANKED_LEAGUE_DATA = exports.HERO_PETS = exports.HEROES = exports.SPELLS = exports.DARK_ELIXIR_SPELLS = exports.ELIXIR_SPELLS = exports.SUPER_TROOPS = exports.SIEGE_MACHINES = exports.HOME_TROOPS = exports.DARK_ELIXIR_TROOPS = exports.ELIXIR_TROOPS = exports.DEV_SITE_API_BASE_URL = exports.API_BASE_URL = void 0;
|
|
4
4
|
exports.API_BASE_URL = 'https://api.clashofclans.com/v1';
|
|
5
5
|
exports.DEV_SITE_API_BASE_URL = 'https://developer.clashofclans.com/api';
|
|
6
6
|
exports.ELIXIR_TROOPS = [
|
|
@@ -115,3 +115,8 @@ exports.EVENTS = {
|
|
|
115
115
|
MAINTENANCE_END: 'maintenanceEnd',
|
|
116
116
|
ERROR: 'error'
|
|
117
117
|
};
|
|
118
|
+
exports.CWL_ROUNDS = {
|
|
119
|
+
PREVIOUS_ROUND: 'warEnded',
|
|
120
|
+
CURRENT_ROUND: 'inWar',
|
|
121
|
+
NEXT_ROUND: 'preparation'
|
|
122
|
+
};
|
package/package.json
CHANGED