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.
@@ -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 (regular or friendly) in the clan. */
44
- getClanWar(clanTag: string, options?: OverrideOptions): Promise<ClanWar | null>;
45
- /** Get info about currently running war in the clan. */
46
- getCurrentWar(clanTag: string, options?: OverrideOptions): Promise<ClanWar | null>;
47
- /** Get info about currently running CWL round. */
48
- getLeagueWar(clanTag: string, round?: keyof typeof CWL_ROUNDS): Promise<ClanWar | null>;
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 | null>;
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 | Type | Description |
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
- * | Name | Type | Description |
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
- export interface ClientEvents {
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 {};
@@ -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 (regular or friendly) in the clan. */
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
- return null;
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
- /** Get info about currently running war in the clan. */
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
- const data = await this.getClanWar(clanTag, options);
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 === 403) {
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
- /** Get info about currently running CWL round. */
86
- async getLeagueWar(clanTag, round) {
87
- const state = (round && Constants_1.CWL_ROUNDS[round]) ?? 'inWar'; // eslint-disable-line
88
- const data = await this.getClanWarLeagueGroup(clanTag);
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
- return wars.find((war) => war?.state === state) ?? wars.at(0) ?? null;
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().getDate();
132
+ const date = new Date().getUTCDate();
133
+ if (!(date >= 1 && date <= 10)) {
134
+ return [await this.getClanWar(clanTag, options)];
135
+ }
108
136
  try {
109
- const data = await this.getClanWar(clanTag, options);
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 (!(date >= 1 && date <= 10))
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
- return [];
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
- return null;
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} class. */
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<(string | symbol)[]>;
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} class. */
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({ name: event.name, fn: event.filter });
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({ name: event.name, fn: event.filter });
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({ name: event.name, fn: event.filter });
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), 100000);
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, fn } of this._events.clans) {
185
+ for (const { name, filter } of this._events.clans) {
180
186
  try {
181
- if (!fn(cached, clan))
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, fn } of this._events.players) {
206
+ for (const { name, filter } of this._events.players) {
201
207
  try {
202
- if (!fn(cached, player))
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, i) => {
220
- const key = `WAR:${i}:${tag}`;
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, fn } of this._events.wars) {
230
+ for (const { name, filter } of this._events.wars) {
225
231
  try {
226
- if (!fn(cached, war))
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
  }
@@ -1,8 +1,26 @@
1
- /** Represents an HTTP Error. */
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
- constructor(error: any, status: number, path: string, method?: string);
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
+ };
@@ -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 a request.',
7
- 404: 'Resource was not found.',
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
- /** Represents an HTTP Error. */
22
+ /**
23
+ * Represents an HTTP Error.
24
+ */
23
25
  class HTTPError extends Error {
24
- constructor(error, status, path, method = 'GET') {
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
  }
@@ -22,6 +22,7 @@ export declare class RequestHandler {
22
22
  data: T;
23
23
  maxAge: number;
24
24
  status: number;
25
+ path: string;
25
26
  }>;
26
27
  private exec;
27
28
  init(options: InitOptions): Promise<string[]>;
@@ -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))
@@ -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
- /** The timestamp when a fresh version of this data will be available again. */
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?: number;
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 `regular`. */
133
- get type(): 'friendly' | 'cwl' | 'regular';
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
  }
@@ -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 = Date.now() + (extra.maxAge ?? 0);
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 `regular`. */
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 'regular';
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' && res.value)
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' && res.value)
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 `regular`. */
53
- get type(): "friendly" | "cwl" | "regular";
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 `regular`. */
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 'regular';
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' && res.value)
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.dc4d83a",
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.0.0",
99
- "@typescript-eslint/parser": "^5.0.0",
100
- "eslint": "^7.28.0",
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.4.3"
106
+ "typescript": "^4.5.2"
107
107
  },
108
108
  "engines": {
109
109
  "node": ">=14.x"