clashofclans.js 2.0.0-dev.dc4d83a → 2.0.0-dev.dedb83d
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/dist/client/Client.d.ts +50 -12
- package/dist/client/Client.js +54 -30
- package/dist/client/EventManager.d.ts +8 -7
- package/dist/client/EventManager.js +35 -13
- package/dist/rest/HTTPError.d.ts +20 -2
- package/dist/rest/HTTPError.js +17 -6
- package/dist/rest/RESTManager.d.ts +24 -0
- package/dist/rest/RequestHandler.d.ts +1 -0
- package/dist/rest/RequestHandler.js +6 -4
- package/dist/struct/ClanWar.d.ts +6 -4
- package/dist/struct/ClanWar.js +17 -3
- package/dist/struct/ClanWarLeagueGroup.js +4 -3
- package/dist/struct/ClanWarLog.d.ts +2 -2
- package/dist/struct/ClanWarLog.js +2 -2
- package/dist/util/Util.js +3 -1
- package/package.json +5 -5
package/dist/client/Client.d.ts
CHANGED
|
@@ -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
|
|
@@ -115,15 +141,21 @@ export declare class Client extends EventEmitter {
|
|
|
115
141
|
private static maintenanceEnd;
|
|
116
142
|
/** @internal */
|
|
117
143
|
on<K extends keyof ClientEvents>(event: K, listeners: (...args: ClientEvents[K]) => void): this;
|
|
144
|
+
/** @internal */
|
|
145
|
+
on<S extends keyof CustomEvents>(event: Exclude<S, keyof ClientEvents>, listeners: (...args: CustomEvents[S]) => void): this;
|
|
118
146
|
/** @internal */ on<S extends string | symbol>(event: Exclude<S, keyof ClientEvents>, listeners: (...args: any[]) => void): this;
|
|
119
147
|
/** @internal */
|
|
120
148
|
once<K extends keyof ClientEvents>(event: K, listeners: (...args: ClientEvents[K]) => void): this;
|
|
149
|
+
/** @internal */
|
|
150
|
+
once<S extends keyof CustomEvents>(event: Exclude<S, keyof ClientEvents>, listeners: (...args: CustomEvents[S]) => void): this;
|
|
121
151
|
/** @internal */ once<S extends string | symbol>(event: Exclude<S, keyof ClientEvents>, listeners: (...args: any[]) => void): this;
|
|
122
152
|
/** @internal */
|
|
123
153
|
emit<K extends keyof ClientEvents>(event: K, ...args: ClientEvents[K]): boolean;
|
|
154
|
+
/** @internal */
|
|
155
|
+
emit<S extends keyof CustomEvents>(event: Exclude<S, keyof ClientEvents>, ...args: CustomEvents[S]): this;
|
|
124
156
|
/** @internal */ emit<S extends string | symbol>(event: Exclude<S, keyof ClientEvents>, ...args: any[]): boolean;
|
|
125
157
|
}
|
|
126
|
-
|
|
158
|
+
interface ClientEvents {
|
|
127
159
|
[EVENTS.NEW_SEASON_START]: [id: string];
|
|
128
160
|
[EVENTS.MAINTENANCE_START]: [];
|
|
129
161
|
[EVENTS.MAINTENANCE_END]: [duration: number];
|
|
@@ -135,3 +167,9 @@ export interface ClientEvents {
|
|
|
135
167
|
[EVENTS.WAR_LOOP_END]: [];
|
|
136
168
|
[EVENTS.ERROR]: [error: unknown];
|
|
137
169
|
}
|
|
170
|
+
interface CustomEvents {
|
|
171
|
+
[key: `clan${string}`]: [oldClan: Clan, newClan: Clan];
|
|
172
|
+
[key: `war${string}`]: [oldWar: ClanWar, newWar: ClanWar];
|
|
173
|
+
[key: `player${string}`]: [oldPlayer: Player, newPlayer: Player];
|
|
174
|
+
}
|
|
175
|
+
export {};
|
package/dist/client/Client.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
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
|
-
if (e instanceof HTTPError_1.HTTPError && e.status
|
|
80
|
-
return this.getLeagueWar(clanTag);
|
|
90
|
+
if (e instanceof HTTPError_1.HTTPError && [200, 403].includes(e.status)) {
|
|
91
|
+
return this.getLeagueWar({ clanTag: args.clanTag, round: args.round });
|
|
81
92
|
}
|
|
93
|
+
throw e;
|
|
82
94
|
}
|
|
83
|
-
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);
|
|
@@ -104,20 +129,18 @@ class Client extends events_1.EventEmitter {
|
|
|
104
129
|
return data._getCurrentWars(clanTag);
|
|
105
130
|
}
|
|
106
131
|
async _getClanWars(clanTag, options) {
|
|
107
|
-
const date = new Date().
|
|
132
|
+
const date = new Date().getUTCDate();
|
|
133
|
+
if (!(date >= 1 && date <= 10)) {
|
|
134
|
+
return [await this.getClanWar(clanTag, options)];
|
|
135
|
+
}
|
|
108
136
|
try {
|
|
109
|
-
|
|
110
|
-
if (!(date >= 1 && date <= 10))
|
|
111
|
-
return data ? [data] : [];
|
|
112
|
-
return data ? [data] : await this._getCurrentLeagueWars(clanTag);
|
|
137
|
+
return this._getCurrentLeagueWars(clanTag);
|
|
113
138
|
}
|
|
114
139
|
catch (e) {
|
|
115
|
-
if (
|
|
116
|
-
return [];
|
|
117
|
-
if (e instanceof HTTPError_1.HTTPError && e.status === 403) {
|
|
118
|
-
return this._getCurrentLeagueWars(clanTag);
|
|
140
|
+
if (e instanceof HTTPError_1.HTTPError && [404].includes(e.status)) {
|
|
141
|
+
return [await this.getClanWar(clanTag)];
|
|
119
142
|
}
|
|
120
|
-
|
|
143
|
+
throw e;
|
|
121
144
|
}
|
|
122
145
|
}
|
|
123
146
|
/** Get information about clan war league. */
|
|
@@ -128,9 +151,10 @@ class Client extends events_1.EventEmitter {
|
|
|
128
151
|
/** Get information about CWL round by WarTag. */
|
|
129
152
|
async getClanWarLeagueRound(warTag, options) {
|
|
130
153
|
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
|
-
|
|
154
|
+
const { data, maxAge, status, path } = await this.rest.getClanWarLeagueRound(args.warTag, options);
|
|
155
|
+
if (data.state === 'notInWar') {
|
|
156
|
+
throw new HTTPError_1.HTTPError(HTTPError_1.NotInWarError, status, path, maxAge);
|
|
157
|
+
}
|
|
134
158
|
return new struct_1.ClanWar(this, data, { warTag: args.warTag, clanTag: args.clanTag, maxAge });
|
|
135
159
|
}
|
|
136
160
|
/** Get information about a player by tag. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Clan, ClanWar, Player } from '../struct';
|
|
2
2
|
import { Client } from './Client';
|
|
3
|
-
/** Represents Event Manager of the {@link Client}
|
|
3
|
+
/** Represents Event Manager of the {@link Client}. */
|
|
4
4
|
export declare class EventManager {
|
|
5
5
|
private readonly client;
|
|
6
6
|
private readonly _clanTags;
|
|
@@ -14,7 +14,7 @@ export declare class EventManager {
|
|
|
14
14
|
private _maintenanceStartTime;
|
|
15
15
|
constructor(client: Client);
|
|
16
16
|
/** Initialize the Event Manager to start pulling. */
|
|
17
|
-
init(): Promise<
|
|
17
|
+
init(): Promise<string[]>;
|
|
18
18
|
/** Add a clan tag to clan events. */
|
|
19
19
|
addClans(...tags: string[]): this;
|
|
20
20
|
/** Delete a clan tag from clan events. */
|
|
@@ -30,6 +30,8 @@ export declare class EventManager {
|
|
|
30
30
|
/**
|
|
31
31
|
* Set your own custom clan event.
|
|
32
32
|
*
|
|
33
|
+
* In order to emit the custom event, you must have this filter function that returns a boolean.
|
|
34
|
+
*
|
|
33
35
|
* @example
|
|
34
36
|
* ```js
|
|
35
37
|
* client.events.addClans(['#2PP', '#8QU8J9LP']);
|
|
@@ -57,6 +59,8 @@ export declare class EventManager {
|
|
|
57
59
|
}): this;
|
|
58
60
|
/**
|
|
59
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.
|
|
60
64
|
*/
|
|
61
65
|
setWarEvent(event: {
|
|
62
66
|
name: string;
|
|
@@ -64,6 +68,8 @@ export declare class EventManager {
|
|
|
64
68
|
}): this;
|
|
65
69
|
/**
|
|
66
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.
|
|
67
73
|
*/
|
|
68
74
|
setPlayerEvent(event: {
|
|
69
75
|
name: string;
|
|
@@ -78,8 +84,3 @@ export declare class EventManager {
|
|
|
78
84
|
private runPlayerUpdate;
|
|
79
85
|
private runWarUpdate;
|
|
80
86
|
}
|
|
81
|
-
export interface EventTypes {
|
|
82
|
-
CLAN: [oldClan: Clan, newClan: Clan];
|
|
83
|
-
PLAYER: [oldPlayer: Player, newPlayer: Player];
|
|
84
|
-
CLAN_WAR: [oldWar: ClanWar, newWar: ClanWar];
|
|
85
|
-
}
|
|
@@ -4,7 +4,7 @@ exports.EventManager = void 0;
|
|
|
4
4
|
const HTTPError_1 = require("../rest/HTTPError");
|
|
5
5
|
const Constants_1 = require("../util/Constants");
|
|
6
6
|
const Util_1 = require("../util/Util");
|
|
7
|
-
/** Represents Event Manager of the {@link Client}
|
|
7
|
+
/** Represents Event Manager of the {@link Client}. */
|
|
8
8
|
class EventManager {
|
|
9
9
|
constructor(client) {
|
|
10
10
|
this.client = client;
|
|
@@ -76,6 +76,8 @@ class EventManager {
|
|
|
76
76
|
/**
|
|
77
77
|
* Set your own custom clan event.
|
|
78
78
|
*
|
|
79
|
+
* In order to emit the custom event, you must have this filter function that returns a boolean.
|
|
80
|
+
*
|
|
79
81
|
* @example
|
|
80
82
|
* ```js
|
|
81
83
|
* client.events.addClans(['#2PP', '#8QU8J9LP']);
|
|
@@ -98,21 +100,25 @@ class EventManager {
|
|
|
98
100
|
* @returns
|
|
99
101
|
*/
|
|
100
102
|
setClanEvent(event) {
|
|
101
|
-
this._events.clans.push(
|
|
103
|
+
this._events.clans.push(event);
|
|
102
104
|
return this;
|
|
103
105
|
}
|
|
104
106
|
/**
|
|
105
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.
|
|
106
110
|
*/
|
|
107
111
|
setWarEvent(event) {
|
|
108
|
-
this._events.wars.push(
|
|
112
|
+
this._events.wars.push(event);
|
|
109
113
|
return this;
|
|
110
114
|
}
|
|
111
115
|
/**
|
|
112
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.
|
|
113
119
|
*/
|
|
114
120
|
setPlayerEvent(event) {
|
|
115
|
-
this._events.players.push(
|
|
121
|
+
this._events.players.push(event);
|
|
116
122
|
return this;
|
|
117
123
|
}
|
|
118
124
|
async maintenanceHandler() {
|
|
@@ -158,7 +164,7 @@ class EventManager {
|
|
|
158
164
|
for (const tag of this._playerTags)
|
|
159
165
|
await this.runPlayerUpdate(tag);
|
|
160
166
|
this.client.emit(Constants_1.EVENTS.PLAYER_LOOP_END);
|
|
161
|
-
setTimeout(this.playerUpdateHandler.bind(this),
|
|
167
|
+
setTimeout(this.playerUpdateHandler.bind(this), 10000);
|
|
162
168
|
}
|
|
163
169
|
async warUpdateHandler() {
|
|
164
170
|
this.client.emit(Constants_1.EVENTS.WAR_LOOP_START);
|
|
@@ -176,9 +182,9 @@ class EventManager {
|
|
|
176
182
|
const cached = this._clans.get(clan.tag);
|
|
177
183
|
if (!cached)
|
|
178
184
|
return this._clans.set(clan.tag, clan);
|
|
179
|
-
for (const { name,
|
|
185
|
+
for (const { name, filter } of this._events.clans) {
|
|
180
186
|
try {
|
|
181
|
-
if (!
|
|
187
|
+
if (!filter(cached, clan))
|
|
182
188
|
continue;
|
|
183
189
|
this.client.emit(name, cached, clan);
|
|
184
190
|
}
|
|
@@ -197,9 +203,9 @@ class EventManager {
|
|
|
197
203
|
const cached = this._players.get(player.tag);
|
|
198
204
|
if (!cached)
|
|
199
205
|
return this._players.set(player.tag, player);
|
|
200
|
-
for (const { name,
|
|
206
|
+
for (const { name, filter } of this._events.players) {
|
|
201
207
|
try {
|
|
202
|
-
if (!
|
|
208
|
+
if (!filter(cached, player))
|
|
203
209
|
continue;
|
|
204
210
|
this.client.emit(name, cached, player);
|
|
205
211
|
}
|
|
@@ -216,14 +222,14 @@ class EventManager {
|
|
|
216
222
|
const clanWars = await this.client._getClanWars(tag).catch(() => null);
|
|
217
223
|
if (!clanWars?.length)
|
|
218
224
|
return null;
|
|
219
|
-
clanWars.forEach((war,
|
|
220
|
-
const key =
|
|
225
|
+
clanWars.forEach(async (war, state) => {
|
|
226
|
+
const key = `${tag}:${state}`;
|
|
221
227
|
const cached = this._wars.get(key);
|
|
222
228
|
if (!cached)
|
|
223
229
|
return this._wars.set(key, war);
|
|
224
|
-
for (const { name,
|
|
230
|
+
for (const { name, filter } of this._events.wars) {
|
|
225
231
|
try {
|
|
226
|
-
if (!
|
|
232
|
+
if (!filter(cached, war))
|
|
227
233
|
continue;
|
|
228
234
|
this.client.emit(name, cached, war);
|
|
229
235
|
}
|
|
@@ -231,6 +237,22 @@ class EventManager {
|
|
|
231
237
|
this.client.emit(Constants_1.EVENTS.ERROR, error);
|
|
232
238
|
}
|
|
233
239
|
}
|
|
240
|
+
// check for war end
|
|
241
|
+
if (state === 1 && cached.warTag !== war.warTag) {
|
|
242
|
+
const data = await this.client.getLeagueWar({ clanTag: tag, round: 'PREVIOUS_ROUND' }).catch(() => null);
|
|
243
|
+
if (data && data.warTag === cached.warTag) {
|
|
244
|
+
for (const { name, filter } of this._events.wars) {
|
|
245
|
+
try {
|
|
246
|
+
if (!filter(cached, data))
|
|
247
|
+
continue;
|
|
248
|
+
this.client.emit(name, cached, data);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
this.client.emit(Constants_1.EVENTS.ERROR, error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
234
256
|
return this._wars.set(key, war);
|
|
235
257
|
});
|
|
236
258
|
}
|
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,10 +1,10 @@
|
|
|
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
5
|
500: 'Unknown error happened when handling the request.',
|
|
6
|
-
504: 'The user aborted
|
|
7
|
-
404: '
|
|
6
|
+
504: 'The user aborted this request.',
|
|
7
|
+
404: 'Requested resource was not found.',
|
|
8
8
|
400: 'Client provided incorrect parameters for the request.',
|
|
9
9
|
503: 'Service is temporarily unavailable because of maintenance.',
|
|
10
10
|
429: 'Request was throttled, because amount of requests was above the threshold defined for the used API token.',
|
|
@@ -19,15 +19,26 @@ const reasons = {
|
|
|
19
19
|
404: 'notFound',
|
|
20
20
|
504: 'requestAborted'
|
|
21
21
|
};
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Represents an HTTP Error.
|
|
24
|
+
*/
|
|
23
25
|
class HTTPError extends Error {
|
|
24
|
-
constructor(error, status, path, method
|
|
26
|
+
constructor(error, status, path, maxAge, method) {
|
|
25
27
|
super();
|
|
26
28
|
this.message = error?.message ?? messages[status];
|
|
27
29
|
this.reason = error?.reason ?? reasons[status];
|
|
28
30
|
this.path = path;
|
|
29
|
-
this.method = method;
|
|
31
|
+
this.method = method ?? 'GET';
|
|
30
32
|
this.status = status;
|
|
33
|
+
this.maxAge = maxAge;
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
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
|
}
|
|
@@ -51,7 +51,7 @@ class RequestHandler {
|
|
|
51
51
|
async request(path, options = {}) {
|
|
52
52
|
const cached = (await this.cached?.get(path)) ?? null;
|
|
53
53
|
if (cached && options.force !== true)
|
|
54
|
-
return { data: cached, maxAge: 0, status: 200 };
|
|
54
|
+
return { data: cached, maxAge: 0, status: 200, path };
|
|
55
55
|
if (!this.throttler || options.ignoreRateLimit)
|
|
56
56
|
return this.exec(path, options);
|
|
57
57
|
await this.throttler.wait();
|
|
@@ -77,12 +77,14 @@ class RequestHandler {
|
|
|
77
77
|
await this.login();
|
|
78
78
|
return this.exec(path, options, ++retries);
|
|
79
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);
|
|
80
83
|
if (!res?.ok)
|
|
81
|
-
throw new HTTPError_1.HTTPError(data, res?.status ?? 504, path, options.method);
|
|
82
|
-
const maxAge = Number(res.headers.get('cache-control')?.split('=')?.[1] ?? 0) * 1000;
|
|
84
|
+
throw new HTTPError_1.HTTPError(data, res?.status ?? 504, path, maxAge, options.method);
|
|
83
85
|
if (this.cached && maxAge > 0 && options.cache !== false)
|
|
84
86
|
await this.cached.set(path, data, maxAge);
|
|
85
|
-
return { data, maxAge, status: res.status };
|
|
87
|
+
return { data, maxAge, status: res.status, path };
|
|
86
88
|
}
|
|
87
89
|
async init(options) {
|
|
88
90
|
if (!(options.email && options.password))
|
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
|
}
|
|
@@ -68,10 +68,11 @@ class ClanWarLeagueGroup {
|
|
|
68
68
|
const warTags = rounds
|
|
69
69
|
.slice(-2)
|
|
70
70
|
.map((round) => round.warTags)
|
|
71
|
-
.flat()
|
|
71
|
+
.flat()
|
|
72
|
+
.reverse();
|
|
72
73
|
const wars = await Promise.allSettled(warTags.map((warTag) => this.client.getClanWarLeagueRound({ warTag, clanTag }, { ignoreRateLimit: true })));
|
|
73
74
|
return wars
|
|
74
|
-
.filter((res) => res.status === 'fulfilled'
|
|
75
|
+
.filter((res) => res.status === 'fulfilled')
|
|
75
76
|
.map((res) => res.value)
|
|
76
77
|
.filter((war) => war.clan.tag === clanTag);
|
|
77
78
|
}
|
|
@@ -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/util/Util.js
CHANGED
|
@@ -18,6 +18,8 @@ class Util extends null {
|
|
|
18
18
|
/** Get URL search params. */
|
|
19
19
|
static queryString(options = {}) {
|
|
20
20
|
const query = new URLSearchParams(options);
|
|
21
|
+
for (const key of ['cache', 'force', 'retryLimit', 'ignoreRateLimit', 'restRequestTimeout'])
|
|
22
|
+
query.delete(key);
|
|
21
23
|
return query.toString();
|
|
22
24
|
}
|
|
23
25
|
static getSeasonEnd(month, autoFix = true) {
|
|
@@ -41,7 +43,7 @@ class Util extends null {
|
|
|
41
43
|
}
|
|
42
44
|
static async allSettled(values) {
|
|
43
45
|
return (await Promise.allSettled(values))
|
|
44
|
-
.filter((res) => res.status === 'fulfilled'
|
|
46
|
+
.filter((res) => res.status === 'fulfilled')
|
|
45
47
|
.map((res) => res.value);
|
|
46
48
|
}
|
|
47
49
|
static async delay(ms) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clashofclans.js",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.dedb83d",
|
|
4
4
|
"description": "JavaScript library for interacting with the Clash of Clans API",
|
|
5
5
|
"author": "SUVAJIT <suvajit.me@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -95,15 +95,15 @@
|
|
|
95
95
|
"@types/keyv": "^3.1.3",
|
|
96
96
|
"@types/node": "^16.10.3",
|
|
97
97
|
"@types/node-fetch": "^2.5.12",
|
|
98
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
99
|
-
"@typescript-eslint/parser": "^5.
|
|
100
|
-
"eslint": "^
|
|
98
|
+
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
|
99
|
+
"@typescript-eslint/parser": "^5.4.0",
|
|
100
|
+
"eslint": "^8.3.0",
|
|
101
101
|
"eslint-config-marine": "^9.0.6",
|
|
102
102
|
"eslint-config-prettier": "^8.3.0",
|
|
103
103
|
"eslint-plugin-prettier": "^4.0.0",
|
|
104
104
|
"prettier": "^2.4.1",
|
|
105
105
|
"rimraf": "^3.0.2",
|
|
106
|
-
"typescript": "^4.
|
|
106
|
+
"typescript": "^4.5.2"
|
|
107
107
|
},
|
|
108
108
|
"engines": {
|
|
109
109
|
"node": ">=14.x"
|