clashofclans.js 2.0.0-dev.0a8e422 → 2.0.0-dev.75d755c

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 CHANGED
@@ -44,7 +44,7 @@ const client = new Client({
44
44
  throttler: new BatchThrottler(30)
45
45
  });
46
46
 
47
- client.events.addClans(['#8P2QG08P']);
47
+ client.events.addClans(['#8QU8J9LP', '#8P2QG08P']);
48
48
  client.events.setClanEvent({
49
49
  name: 'clanDescriptionChange',
50
50
  filter: (oldClan, newClan) => {
@@ -155,8 +155,7 @@ export declare class Client extends EventEmitter {
155
155
  emit<S extends keyof CustomEvents>(event: Exclude<S, keyof ClientEvents>, ...args: CustomEvents[S]): this;
156
156
  /** @internal */ emit<S extends string | symbol>(event: Exclude<S, keyof ClientEvents>, ...args: any[]): boolean;
157
157
  }
158
- /** Client events that can be emitted by the client. */
159
- export interface ClientEvents {
158
+ interface ClientEvents {
160
159
  [EVENTS.NEW_SEASON_START]: [id: string];
161
160
  [EVENTS.MAINTENANCE_START]: [];
162
161
  [EVENTS.MAINTENANCE_END]: [duration: number];
@@ -168,13 +167,9 @@ export interface ClientEvents {
168
167
  [EVENTS.WAR_LOOP_END]: [];
169
168
  [EVENTS.ERROR]: [error: unknown];
170
169
  }
171
- /**
172
- * Custom events that can be emitted by the client.
173
- *
174
- * TypeScript 4.5 now can narrow values that have template string types, and also recognizes template string types as discriminants.
175
- */
176
- export interface CustomEvents {
170
+ interface CustomEvents {
177
171
  [key: `clan${string}`]: [oldClan: Clan, newClan: Clan];
178
- [key: `player${string}`]: [oldClan: Player, newClan: Player];
179
- [key: `war${string}`]: [oldClan: ClanWar, newClan: ClanWar];
172
+ [key: `war${string}`]: [oldWar: ClanWar, newWar: ClanWar];
173
+ [key: `player${string}`]: [oldPlayer: Player, newPlayer: Player];
180
174
  }
175
+ export {};
@@ -66,7 +66,7 @@ class Client extends events_1.EventEmitter {
66
66
  async getClanWar(clanTag, options) {
67
67
  const { data, maxAge, path, status } = await this.rest.getCurrentWar(clanTag, options);
68
68
  if (data.state === 'notInWar') {
69
- throw new HTTPError_1.HTTPError(HTTPError_1.notInWarError, status, path, maxAge);
69
+ throw new HTTPError_1.HTTPError(HTTPError_1.NotInWarError, status, path, maxAge);
70
70
  }
71
71
  return new struct_1.ClanWar(this, data, { clanTag, maxAge });
72
72
  }
@@ -87,11 +87,11 @@ class Client extends events_1.EventEmitter {
87
87
  return await this.getClanWar(args.clanTag, options);
88
88
  }
89
89
  catch (e) {
90
- if (e instanceof HTTPError_1.HTTPError && e.status === 403) {
90
+ if (e instanceof HTTPError_1.HTTPError && [200, 403].includes(e.status)) {
91
91
  return this.getLeagueWar({ clanTag: args.clanTag, round: args.round });
92
92
  }
93
+ throw e;
93
94
  }
94
- return null;
95
95
  }
96
96
  /**
97
97
  * Get info about currently running CWL round.
@@ -129,17 +129,18 @@ class Client extends events_1.EventEmitter {
129
129
  return data._getCurrentWars(clanTag);
130
130
  }
131
131
  async _getClanWars(clanTag, options) {
132
- const date = new Date().getDate();
133
- try {
132
+ const date = new Date().getUTCDate();
133
+ if (!(date >= 1 && date <= 10)) {
134
134
  return [await this.getClanWar(clanTag, options)];
135
135
  }
136
+ try {
137
+ return this._getCurrentLeagueWars(clanTag);
138
+ }
136
139
  catch (e) {
137
- if (!(date >= 1 && date <= 10))
138
- return [];
139
- if (e instanceof HTTPError_1.HTTPError && [200, 403].includes(e.status)) {
140
- return this._getCurrentLeagueWars(clanTag);
140
+ if (e instanceof HTTPError_1.HTTPError && [404].includes(e.status)) {
141
+ return [await this.getClanWar(clanTag)];
141
142
  }
142
- return [];
143
+ throw e;
143
144
  }
144
145
  }
145
146
  /** Get information about clan war league. */
@@ -152,7 +153,7 @@ class Client extends events_1.EventEmitter {
152
153
  const args = typeof warTag === 'string' ? { warTag } : { warTag: warTag.warTag, clanTag: warTag.clanTag };
153
154
  const { data, maxAge, status, path } = await this.rest.getClanWarLeagueRound(args.warTag, options);
154
155
  if (data.state === 'notInWar') {
155
- throw new HTTPError_1.HTTPError(HTTPError_1.notInWarError, status, path, maxAge);
156
+ throw new HTTPError_1.HTTPError(HTTPError_1.NotInWarError, status, path, maxAge);
156
157
  }
157
158
  return new struct_1.ClanWar(this, data, { warTag: args.warTag, clanTag: args.clanTag, maxAge });
158
159
  }
@@ -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;
@@ -15,18 +15,18 @@ export declare class EventManager {
15
15
  constructor(client: Client);
16
16
  /** Initialize the Event Manager to start pulling. */
17
17
  init(): Promise<string[]>;
18
- /** Add a clan tag to clan events. */
19
- addClans(...tags: string[]): this;
20
- /** Delete a clan tag from clan events. */
21
- deleteClans(...tags: string[]): this;
22
- /** Add a player tag for player events. */
23
- addPlayers(...tags: string[]): this;
24
- /** Delete a player tag from player events. */
25
- deletePlayers(...tags: string[]): this;
26
- /** Add a clan tag for war events. */
27
- addWars(...tags: string[]): this;
28
- /** Delete a clan tag from war events. */
29
- deleteWars(...tags: string[]): this;
18
+ /** Add clan tags to clan events. */
19
+ addClans(tags: string[] | string): this;
20
+ /** Delete clan tags from clan events. */
21
+ deleteClans(tags: string[] | string): this;
22
+ /** Add player tags for player events. */
23
+ addPlayers(tags: string[] | string): this;
24
+ /** Delete player tags from player events. */
25
+ deletePlayers(tags: string[] | string): this;
26
+ /** Add clan tags for war events. */
27
+ addWars(tags: string[] | string): this;
28
+ /** Delete clan tags from war events. */
29
+ deleteWars(tags: string[] | string): this;
30
30
  /**
31
31
  * Set your own custom clan event.
32
32
  *
@@ -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;
@@ -31,45 +31,52 @@ class EventManager {
31
31
  this.warUpdateHandler();
32
32
  return Promise.resolve(this.client.eventNames());
33
33
  }
34
- /** Add a clan tag to clan events. */
35
- addClans(...tags) {
36
- for (const tag of tags) {
34
+ /** Add clan tags to clan events. */
35
+ addClans(tags) {
36
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
37
37
  this._clanTags.add(this.client.util.parseTag(tag));
38
38
  }
39
39
  return this;
40
40
  }
41
- /** Delete a clan tag from clan events. */
42
- deleteClans(...tags) {
43
- for (const tag of tags) {
44
- this._warTags.delete(this.client.util.parseTag(tag));
41
+ /** Delete clan tags from clan events. */
42
+ deleteClans(tags) {
43
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
44
+ const key = this.client.util.parseTag(tag);
45
+ this._clans.delete(key);
46
+ this._clanTags.delete(key);
45
47
  }
46
48
  return this;
47
49
  }
48
- /** Add a player tag for player events. */
49
- addPlayers(...tags) {
50
- for (const tag of tags) {
50
+ /** Add player tags for player events. */
51
+ addPlayers(tags) {
52
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
51
53
  this._playerTags.add(this.client.util.parseTag(tag));
52
54
  }
53
55
  return this;
54
56
  }
55
- /** Delete a player tag from player events. */
56
- deletePlayers(...tags) {
57
- for (const tag of tags) {
58
- this._warTags.delete(this.client.util.parseTag(tag));
57
+ /** Delete player tags from player events. */
58
+ deletePlayers(tags) {
59
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
60
+ const key = this.client.util.parseTag(tag);
61
+ this._players.delete(key);
62
+ this._playerTags.delete(key);
59
63
  }
60
64
  return this;
61
65
  }
62
- /** Add a clan tag for war events. */
63
- addWars(...tags) {
64
- for (const tag of tags) {
66
+ /** Add clan tags for war events. */
67
+ addWars(tags) {
68
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
65
69
  this._warTags.add(this.client.util.parseTag(tag));
66
70
  }
67
71
  return this;
68
72
  }
69
- /** Delete a clan tag from war events. */
70
- deleteWars(...tags) {
71
- for (const tag of tags) {
72
- this._warTags.delete(this.client.util.parseTag(tag));
73
+ /** Delete clan tags from war events. */
74
+ deleteWars(tags) {
75
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
76
+ const key = this.client.util.parseTag(tag);
77
+ this._wars.delete(`${key}:${1}`);
78
+ this._wars.delete(`${key}:${2}`);
79
+ this._warTags.delete(key);
73
80
  }
74
81
  return this;
75
82
  }
@@ -100,7 +107,7 @@ class EventManager {
100
107
  * @returns
101
108
  */
102
109
  setClanEvent(event) {
103
- this._events.clans.push({ name: event.name, fn: event.filter });
110
+ this._events.clans.push(event);
104
111
  return this;
105
112
  }
106
113
  /**
@@ -109,7 +116,7 @@ class EventManager {
109
116
  * In order to emit the custom event, you must have this filter function that returns a boolean.
110
117
  */
111
118
  setWarEvent(event) {
112
- this._events.wars.push({ name: event.name, fn: event.filter });
119
+ this._events.wars.push(event);
113
120
  return this;
114
121
  }
115
122
  /**
@@ -118,7 +125,7 @@ class EventManager {
118
125
  * In order to emit the custom event, you must have this filter function that returns a boolean.
119
126
  */
120
127
  setPlayerEvent(event) {
121
- this._events.players.push({ name: event.name, fn: event.filter });
128
+ this._events.players.push(event);
122
129
  return this;
123
130
  }
124
131
  async maintenanceHandler() {
@@ -164,7 +171,7 @@ class EventManager {
164
171
  for (const tag of this._playerTags)
165
172
  await this.runPlayerUpdate(tag);
166
173
  this.client.emit(Constants_1.EVENTS.PLAYER_LOOP_END);
167
- setTimeout(this.playerUpdateHandler.bind(this), 100000);
174
+ setTimeout(this.playerUpdateHandler.bind(this), 10000);
168
175
  }
169
176
  async warUpdateHandler() {
170
177
  this.client.emit(Constants_1.EVENTS.WAR_LOOP_START);
@@ -182,9 +189,9 @@ class EventManager {
182
189
  const cached = this._clans.get(clan.tag);
183
190
  if (!cached)
184
191
  return this._clans.set(clan.tag, clan);
185
- for (const { name, fn } of this._events.clans) {
192
+ for (const { name, filter } of this._events.clans) {
186
193
  try {
187
- if (!fn(cached, clan))
194
+ if (!filter(cached, clan))
188
195
  continue;
189
196
  this.client.emit(name, cached, clan);
190
197
  }
@@ -203,9 +210,9 @@ class EventManager {
203
210
  const cached = this._players.get(player.tag);
204
211
  if (!cached)
205
212
  return this._players.set(player.tag, player);
206
- for (const { name, fn } of this._events.players) {
213
+ for (const { name, filter } of this._events.players) {
207
214
  try {
208
- if (!fn(cached, player))
215
+ if (!filter(cached, player))
209
216
  continue;
210
217
  this.client.emit(name, cached, player);
211
218
  }
@@ -222,14 +229,14 @@ class EventManager {
222
229
  const clanWars = await this.client._getClanWars(tag).catch(() => null);
223
230
  if (!clanWars?.length)
224
231
  return null;
225
- clanWars.forEach((war, i) => {
226
- const key = `WAR:${i}:${tag}`;
232
+ clanWars.forEach(async (war, state) => {
233
+ const key = `${tag}:${state}`;
227
234
  const cached = this._wars.get(key);
228
235
  if (!cached)
229
236
  return this._wars.set(key, war);
230
- for (const { name, fn } of this._events.wars) {
237
+ for (const { name, filter } of this._events.wars) {
231
238
  try {
232
- if (!fn(cached, war))
239
+ if (!filter(cached, war))
233
240
  continue;
234
241
  this.client.emit(name, cached, war);
235
242
  }
@@ -237,6 +244,22 @@ class EventManager {
237
244
  this.client.emit(Constants_1.EVENTS.ERROR, error);
238
245
  }
239
246
  }
247
+ // check for war end
248
+ if (state === 1 && cached.warTag !== war.warTag) {
249
+ const data = await this.client.getLeagueWar({ clanTag: tag, round: 'PREVIOUS_ROUND' }).catch(() => null);
250
+ if (data && data.warTag === cached.warTag) {
251
+ for (const { name, filter } of this._events.wars) {
252
+ try {
253
+ if (!filter(cached, data))
254
+ continue;
255
+ this.client.emit(name, cached, data);
256
+ }
257
+ catch (error) {
258
+ this.client.emit(Constants_1.EVENTS.ERROR, error);
259
+ }
260
+ }
261
+ }
262
+ }
240
263
  return this._wars.set(key, war);
241
264
  });
242
265
  }
@@ -16,11 +16,11 @@ export declare class HTTPError extends Error {
16
16
  maxAge: number;
17
17
  constructor(error: any, status: number, path: string, maxAge: number, method?: string);
18
18
  }
19
- export declare const notInWarError: {
19
+ export declare const NotInWarError: {
20
20
  message: string;
21
21
  reason: string;
22
22
  };
23
- export declare const privateWarLogError: {
23
+ export declare const PrivateWarLogError: {
24
24
  message: string;
25
25
  reason: string;
26
26
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.privateWarLogError = exports.notInWarError = 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
6
  504: 'The user aborted this request.',
@@ -34,11 +34,11 @@ class HTTPError extends Error {
34
34
  }
35
35
  }
36
36
  exports.HTTPError = HTTPError;
37
- exports.notInWarError = {
37
+ exports.NotInWarError = {
38
38
  message: 'Clan is not in war at this moment.',
39
39
  reason: 'notInWar'
40
40
  };
41
- exports.privateWarLogError = {
41
+ exports.PrivateWarLogError = {
42
42
  message: 'Access denied, clan war log is private.',
43
43
  reason: 'privateWarLog'
44
44
  };
@@ -11,9 +11,9 @@ export declare class RequestHandler {
11
11
  private keys;
12
12
  private readonly baseURL;
13
13
  private readonly retryLimit;
14
- private readonly cached;
15
14
  private readonly restRequestTimeout;
16
15
  private readonly throttler?;
16
+ private readonly cached;
17
17
  constructor(options?: ClientOptions);
18
18
  private get _keys();
19
19
  private get _key();
@@ -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, path };
54
+ return { data: cached.data, maxAge: cached.ttl - Date.now(), status: 200, path };
55
55
  if (!this.throttler || options.ignoreRateLimit)
56
56
  return this.exec(path, options);
57
57
  await this.throttler.wait();
@@ -79,11 +79,12 @@ class RequestHandler {
79
79
  }
80
80
  const maxAge = Number(res?.headers.get('cache-control')?.split('=')?.[1] ?? 0) * 1000;
81
81
  if (res?.status === 403 && !data?.message)
82
- throw new HTTPError_1.HTTPError(HTTPError_1.privateWarLogError, res.status, path, maxAge);
82
+ throw new HTTPError_1.HTTPError(HTTPError_1.PrivateWarLogError, res.status, path, maxAge);
83
83
  if (!res?.ok)
84
84
  throw new HTTPError_1.HTTPError(data, res?.status ?? 504, path, maxAge, options.method);
85
- if (this.cached && maxAge > 0 && options.cache !== false)
86
- await this.cached.set(path, data, maxAge);
85
+ if (this.cached && maxAge > 0 && options.cache !== false) {
86
+ await this.cached.set(path, { data, ttl: Date.now() + maxAge }, maxAge);
87
+ }
87
88
  return { data, maxAge, status: res.status, path };
88
89
  }
89
90
  async init(options) {
@@ -68,7 +68,8 @@ 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
75
  .filter((res) => res.status === 'fulfilled')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clashofclans.js",
3
- "version": "2.0.0-dev.0a8e422",
3
+ "version": "2.0.0-dev.75d755c",
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",