clashofclans.js 3.0.0-dev.392ca4c → 3.0.0-dev.f6ce42f

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.
@@ -1,8 +1,8 @@
1
1
  /// <reference types="node" />
2
+ import { EventEmitter } from 'node:events';
2
3
  import { ClanSearchOptions, SearchOptions, ClientOptions, LoginOptions, OverrideOptions } from '../types';
3
4
  import { CWLRounds } from '../util/Constants';
4
5
  import { RESTManager } from '../rest/RESTManager';
5
- import { EventEmitter } from 'events';
6
6
  import { Util } from '../util/Util';
7
7
  import { Clan, ClanMember, ClanWar, ClanWarLog, League, Location, Player, WarLeague, RankedClan, RankedPlayer, Label, SeasonRankedPlayer, GoldPassSeason, ClanWarLeagueGroup } from '../struct';
8
8
  /**
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Client = void 0;
4
+ const node_events_1 = require("node:events");
4
5
  const Constants_1 = require("../util/Constants");
5
6
  const HTTPError_1 = require("../rest/HTTPError");
6
7
  const RESTManager_1 = require("../rest/RESTManager");
7
- const events_1 = require("events");
8
8
  const Util_1 = require("../util/Util");
9
9
  const struct_1 = require("../struct");
10
10
  /**
@@ -14,7 +14,7 @@ const struct_1 = require("../struct");
14
14
  * const client = new Client({ keys: ['***'] });
15
15
  * ```
16
16
  */
17
- class Client extends events_1.EventEmitter {
17
+ class Client extends node_events_1.EventEmitter {
18
18
  constructor(options) {
19
19
  super();
20
20
  this.rest = new RESTManager_1.RESTManager({ ...options, rejectIfNotValid: true });
@@ -140,7 +140,7 @@ class Client extends events_1.EventEmitter {
140
140
  return [await this.getClanWar(clanTag, options)];
141
141
  }
142
142
  try {
143
- return this.getLeagueWars(clanTag, options);
143
+ return await this.getLeagueWars(clanTag, options);
144
144
  }
145
145
  catch (e) {
146
146
  if (e instanceof HTTPError_1.HTTPError && [404].includes(e.status)) {
@@ -2,7 +2,6 @@ import { Clan, ClanWar, Player } from '../struct';
2
2
  import { ClientOptions } from '../types';
3
3
  import { PollingEvents } from '../util/Constants';
4
4
  import { Client } from './Client';
5
- import { PollingEventManager } from './EventManager';
6
5
  /**
7
6
  * Represents Clash of Clans Polling Event Client.
8
7
  * ```js
@@ -11,11 +10,86 @@ import { PollingEventManager } from './EventManager';
11
10
  * ```
12
11
  */
13
12
  export declare class PollingClient extends Client {
14
- /** Polling Event Manager for the client. */
15
- pollingEvents: PollingEventManager;
13
+ private readonly _clanTags;
14
+ private readonly _playerTags;
15
+ private readonly _warTags;
16
+ private readonly _clans;
17
+ private readonly _players;
18
+ private readonly _wars;
19
+ private readonly _pollingEvents;
20
+ inMaintenance: boolean;
21
+ private _maintenanceStartTime;
16
22
  constructor(options?: ClientOptions);
17
- /** Whether the API is in maintenance break. */
18
- get inMaintenance(): any;
23
+ /** Initialize the PollingEvent Manager to start pulling the data by polling api. */
24
+ init(): Promise<string[]>;
25
+ /** Add clan tags to clan polling events. */
26
+ addClans(tags: string[] | string): this;
27
+ /** Delete clan tags from clan polling events. */
28
+ deleteClans(tags: string[] | string): this;
29
+ /** Add player tags for player polling events. */
30
+ addPlayers(tags: string[] | string): this;
31
+ /** Delete player tags from player polling events. */
32
+ deletePlayers(tags: string[] | string): this;
33
+ /** Add clan tags for war polling events. */
34
+ addWars(tags: string[] | string): this;
35
+ /** Delete clan tags from war polling events. */
36
+ deleteWars(tags: string[] | string): this;
37
+ /**
38
+ * Set your own custom clan polling event.
39
+ *
40
+ * In order to emit the custom polling event, you must have this filter function that returns a boolean.
41
+ *
42
+ * @example
43
+ * ```js
44
+ * client.pollingEvents.addClans(['#2PP', '#8QU8J9LP']);
45
+ *
46
+ * client.pollingEvents.setClanEvent({
47
+ * name: 'clanMemberUpdate',
48
+ * filter: (oldClan, newClan) => {
49
+ * return oldClan.memberCount !== newClan.memberCount;
50
+ * }
51
+ * });
52
+ *
53
+ * client.on('clanMemberUpdate', (oldClan, newClan) => {
54
+ * console.log(oldClan.memberCount, newClan.memberCount);
55
+ * });
56
+ *
57
+ * (async function () {
58
+ * await client.pollingEvents.init();
59
+ * })();
60
+ * ```
61
+ * @returns
62
+ */
63
+ setClanEvent(event: {
64
+ name: string;
65
+ filter: (oldClan: Clan, newClan: Clan) => boolean;
66
+ }): this;
67
+ /**
68
+ * Set your own custom war event.
69
+ *
70
+ * In order to emit the custom event, you must have this filter function that returns a boolean.
71
+ */
72
+ setWarEvent(event: {
73
+ name: string;
74
+ filter: (oldWar: ClanWar, newWar: ClanWar) => boolean;
75
+ }): this;
76
+ /**
77
+ * Set your own custom player event.
78
+ *
79
+ * In order to emit the custom event, you must have this filter function that returns a boolean.
80
+ */
81
+ setPlayerEvent(event: {
82
+ name: string;
83
+ filter: (oldPlayer: Player, newPlayer: Player) => boolean;
84
+ }): this;
85
+ private maintenanceHandler;
86
+ private seasonEndHandler;
87
+ private clanUpdateHandler;
88
+ private playerUpdateHandler;
89
+ private warUpdateHandler;
90
+ private runClanUpdate;
91
+ private runPlayerUpdate;
92
+ private runWarUpdate;
19
93
  /**
20
94
  * Emits when a new season starts.
21
95
  *
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PollingClient = void 0;
4
+ const HTTPError_1 = require("../rest/HTTPError");
4
5
  const Constants_1 = require("../util/Constants");
5
6
  const Client_1 = require("./Client");
6
- const EventManager_1 = require("./EventManager");
7
7
  /**
8
8
  * Represents Clash of Clans Polling Event Client.
9
9
  * ```js
@@ -14,12 +14,271 @@ const EventManager_1 = require("./EventManager");
14
14
  class PollingClient extends Client_1.Client {
15
15
  constructor(options) {
16
16
  super(options);
17
- this.pollingEvents = new EventManager_1.PollingEventManager(this);
17
+ this._clanTags = new Set();
18
+ this._playerTags = new Set();
19
+ this._warTags = new Set();
20
+ this._clans = new Map();
21
+ this._players = new Map();
22
+ this._wars = new Map();
23
+ this._pollingEvents = {
24
+ clans: [],
25
+ wars: [],
26
+ players: []
27
+ };
28
+ this.inMaintenance = Boolean(false);
29
+ this._maintenanceStartTime = null;
18
30
  }
19
- /** Whether the API is in maintenance break. */
20
- get inMaintenance() {
21
- // @ts-expect-error
22
- return this.events._inMaintenance;
31
+ /** Initialize the PollingEvent Manager to start pulling the data by polling api. */
32
+ async init() {
33
+ this.seasonEndHandler();
34
+ this.maintenanceHandler();
35
+ this.clanUpdateHandler();
36
+ this.playerUpdateHandler();
37
+ this.warUpdateHandler();
38
+ return Promise.resolve(this.eventNames());
39
+ }
40
+ /** Add clan tags to clan polling events. */
41
+ addClans(tags) {
42
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
43
+ this._clanTags.add(this.util.formatTag(tag));
44
+ }
45
+ return this;
46
+ }
47
+ /** Delete clan tags from clan polling events. */
48
+ deleteClans(tags) {
49
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
50
+ const key = this.util.formatTag(tag);
51
+ this._clans.delete(key);
52
+ this._clanTags.delete(key);
53
+ }
54
+ return this;
55
+ }
56
+ /** Add player tags for player polling events. */
57
+ addPlayers(tags) {
58
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
59
+ this._playerTags.add(this.util.formatTag(tag));
60
+ }
61
+ return this;
62
+ }
63
+ /** Delete player tags from player polling events. */
64
+ deletePlayers(tags) {
65
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
66
+ const key = this.util.formatTag(tag);
67
+ this._players.delete(key);
68
+ this._playerTags.delete(key);
69
+ }
70
+ return this;
71
+ }
72
+ /** Add clan tags for war polling events. */
73
+ addWars(tags) {
74
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
75
+ this._warTags.add(this.util.formatTag(tag));
76
+ }
77
+ return this;
78
+ }
79
+ /** Delete clan tags from war polling events. */
80
+ deleteWars(tags) {
81
+ for (const tag of Array.isArray(tags) ? tags : [tags]) {
82
+ const key = this.util.formatTag(tag);
83
+ this._wars.delete(`${key}:${1}`);
84
+ this._wars.delete(`${key}:${2}`);
85
+ this._warTags.delete(key);
86
+ }
87
+ return this;
88
+ }
89
+ /**
90
+ * Set your own custom clan polling event.
91
+ *
92
+ * In order to emit the custom polling event, you must have this filter function that returns a boolean.
93
+ *
94
+ * @example
95
+ * ```js
96
+ * client.pollingEvents.addClans(['#2PP', '#8QU8J9LP']);
97
+ *
98
+ * client.pollingEvents.setClanEvent({
99
+ * name: 'clanMemberUpdate',
100
+ * filter: (oldClan, newClan) => {
101
+ * return oldClan.memberCount !== newClan.memberCount;
102
+ * }
103
+ * });
104
+ *
105
+ * client.on('clanMemberUpdate', (oldClan, newClan) => {
106
+ * console.log(oldClan.memberCount, newClan.memberCount);
107
+ * });
108
+ *
109
+ * (async function () {
110
+ * await client.pollingEvents.init();
111
+ * })();
112
+ * ```
113
+ * @returns
114
+ */
115
+ setClanEvent(event) {
116
+ if (!event.name)
117
+ throw new Error('Event name is required.');
118
+ if (typeof event.filter !== 'function')
119
+ throw new Error('Filter function is required.');
120
+ this._pollingEvents.clans.push(event);
121
+ return this;
122
+ }
123
+ /**
124
+ * Set your own custom war event.
125
+ *
126
+ * In order to emit the custom event, you must have this filter function that returns a boolean.
127
+ */
128
+ setWarEvent(event) {
129
+ if (!event.name)
130
+ throw new Error('Event name is required.');
131
+ if (typeof event.filter !== 'function')
132
+ throw new Error('Filter function is required.');
133
+ this._pollingEvents.wars.push(event);
134
+ return this;
135
+ }
136
+ /**
137
+ * Set your own custom player event.
138
+ *
139
+ * In order to emit the custom event, you must have this filter function that returns a boolean.
140
+ */
141
+ setPlayerEvent(event) {
142
+ if (!event.name)
143
+ throw new Error('Event name is required.');
144
+ if (typeof event.filter !== 'function')
145
+ throw new Error('Filter function is required.');
146
+ this._pollingEvents.players.push(event);
147
+ return this;
148
+ }
149
+ async maintenanceHandler() {
150
+ setTimeout(this.maintenanceHandler.bind(this), 10000).unref();
151
+ try {
152
+ const res = await this.rest.getClans({ maxMembers: Math.floor(Math.random() * 40) + 10, limit: 1 });
153
+ if (res.status === 200 && this.inMaintenance) {
154
+ this.inMaintenance = Boolean(false);
155
+ const duration = Date.now() - this._maintenanceStartTime.getTime();
156
+ this._maintenanceStartTime = null;
157
+ this.emit(Constants_1.PollingEvents.MaintenanceEnd, duration);
158
+ }
159
+ }
160
+ catch (error) {
161
+ if (error instanceof HTTPError_1.HTTPError && error.status === 503 && !this.inMaintenance) {
162
+ this.inMaintenance = Boolean(true);
163
+ this._maintenanceStartTime = new Date();
164
+ this.emit(Constants_1.PollingEvents.MaintenanceStart);
165
+ }
166
+ }
167
+ }
168
+ seasonEndHandler() {
169
+ const end = this.util.getSeasonEndTime().getTime() - Date.now();
170
+ // Why this? setTimeout can be up to 24.8 days or 2147483647ms [(2^31 - 1) Max 32bit Integer]
171
+ if (end > 24 * 60 * 60 * 1000) {
172
+ setTimeout(this.seasonEndHandler.bind(this), 60 * 60 * 1000);
173
+ }
174
+ else if (end > 0) {
175
+ setTimeout(() => {
176
+ this.emit(Constants_1.PollingEvents.NewSeasonStart, this.util.getSeasonId());
177
+ }, end + 100).unref();
178
+ }
179
+ }
180
+ async clanUpdateHandler() {
181
+ this.emit(Constants_1.PollingEvents.ClanLoopStart);
182
+ for (const tag of this._clanTags)
183
+ await this.runClanUpdate(tag);
184
+ this.emit(Constants_1.PollingEvents.ClanLoopEnd);
185
+ setTimeout(this.clanUpdateHandler.bind(this), 10000);
186
+ }
187
+ async playerUpdateHandler() {
188
+ this.emit(Constants_1.PollingEvents.PlayerLoopStart);
189
+ for (const tag of this._playerTags)
190
+ await this.runPlayerUpdate(tag);
191
+ this.emit(Constants_1.PollingEvents.PlayerLoopEnd);
192
+ setTimeout(this.playerUpdateHandler.bind(this), 10000);
193
+ }
194
+ async warUpdateHandler() {
195
+ this.emit(Constants_1.PollingEvents.WarLoopStart);
196
+ for (const tag of this._warTags)
197
+ await this.runWarUpdate(tag);
198
+ this.emit(Constants_1.PollingEvents.WarLoopEnd);
199
+ setTimeout(this.warUpdateHandler.bind(this), 10000);
200
+ }
201
+ async runClanUpdate(tag) {
202
+ if (this.inMaintenance)
203
+ return null;
204
+ const clan = await this.getClan(tag).catch(() => null);
205
+ if (!clan)
206
+ return null;
207
+ const cached = this._clans.get(clan.tag);
208
+ if (!cached)
209
+ return this._clans.set(clan.tag, clan);
210
+ for (const { name, filter } of this._pollingEvents.clans) {
211
+ try {
212
+ if (!(await filter(cached, clan)))
213
+ continue;
214
+ this.emit(name, cached, clan);
215
+ }
216
+ catch (error) {
217
+ this.emit(Constants_1.PollingEvents.Error, error);
218
+ }
219
+ }
220
+ return this._clans.set(clan.tag, clan);
221
+ }
222
+ async runPlayerUpdate(tag) {
223
+ if (this.inMaintenance)
224
+ return null;
225
+ const player = await this.getPlayer(tag).catch(() => null);
226
+ if (!player)
227
+ return null;
228
+ const cached = this._players.get(player.tag);
229
+ if (!cached)
230
+ return this._players.set(player.tag, player);
231
+ for (const { name, filter } of this._pollingEvents.players) {
232
+ try {
233
+ if (!(await filter(cached, player)))
234
+ continue;
235
+ this.emit(name, cached, player);
236
+ }
237
+ catch (error) {
238
+ this.emit(Constants_1.PollingEvents.Error, error);
239
+ }
240
+ }
241
+ return this._players.set(player.tag, player);
242
+ }
243
+ async runWarUpdate(tag) {
244
+ if (this.inMaintenance)
245
+ return null;
246
+ const clanWars = await this.getWars(tag).catch(() => null);
247
+ if (!clanWars?.length)
248
+ return null;
249
+ clanWars.forEach(async (war, index) => {
250
+ const key = `${tag}:${index}`;
251
+ const cached = this._wars.get(key);
252
+ if (!cached)
253
+ return this._wars.set(key, war);
254
+ for (const { name, filter } of this._pollingEvents.wars) {
255
+ try {
256
+ if (!(await filter(cached, war)))
257
+ continue;
258
+ this.emit(name, cached, war);
259
+ }
260
+ catch (error) {
261
+ this.emit(Constants_1.PollingEvents.Error, error);
262
+ }
263
+ }
264
+ // check for war end
265
+ if (index === 1 && cached.warTag !== war.warTag) {
266
+ const data = await this.getLeagueWar({ clanTag: tag, round: 'PreviousRound' }).catch(() => null);
267
+ if (data && data.warTag === cached.warTag) {
268
+ for (const { name, filter } of this._pollingEvents.wars) {
269
+ try {
270
+ if (!(await filter(cached, data)))
271
+ continue;
272
+ this.emit(name, cached, data);
273
+ }
274
+ catch (error) {
275
+ this.emit(Constants_1.PollingEvents.Error, error);
276
+ }
277
+ }
278
+ }
279
+ }
280
+ return this._wars.set(key, war);
281
+ });
23
282
  }
24
283
  }
25
284
  exports.PollingClient = PollingClient;
package/dist/index.d.ts CHANGED
@@ -3,7 +3,6 @@ export * from './client/PollingClient';
3
3
  export * from './rest/RESTManager';
4
4
  export * from './rest/RequestHandler';
5
5
  export * from './rest/HTTPError';
6
- export * from './client/EventManager';
7
6
  export * from './rest/Throttler';
8
7
  export * from './util/Util';
9
8
  export * from './struct';
package/dist/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -15,7 +19,6 @@ __exportStar(require("./client/PollingClient"), exports);
15
19
  __exportStar(require("./rest/RESTManager"), exports);
16
20
  __exportStar(require("./rest/RequestHandler"), exports);
17
21
  __exportStar(require("./rest/HTTPError"), exports);
18
- __exportStar(require("./client/EventManager"), exports);
19
22
  __exportStar(require("./rest/Throttler"), exports);
20
23
  __exportStar(require("./util/Util"), exports);
21
24
  __exportStar(require("./struct"), exports);
package/dist/index.mjs CHANGED
@@ -43,7 +43,6 @@ export const NotInWarError = mod.NotInWarError;
43
43
  export const Player = mod.Player;
44
44
  export const PlayerClan = mod.PlayerClan;
45
45
  export const PollingClient = mod.PollingClient;
46
- export const PollingEventManager = mod.PollingEventManager;
47
46
  export const PollingEvents = mod.PollingEvents;
48
47
  export const PrivateWarLogError = mod.PrivateWarLogError;
49
48
  export const QueueThrottler = mod.QueueThrottler;
@@ -52,6 +51,7 @@ export const RankedClan = mod.RankedClan;
52
51
  export const RankedPlayer = mod.RankedPlayer;
53
52
  export const RawData = mod.RawData;
54
53
  export const RequestHandler = mod.RequestHandler;
54
+ export const RestEvents = mod.RestEvents;
55
55
  export const Season = mod.Season;
56
56
  export const SeasonRankedPlayer = mod.SeasonRankedPlayer;
57
57
  export const SiegeMachines = mod.SiegeMachines;
@@ -1,6 +1,6 @@
1
- import { RequestHandler } from './RequestHandler';
2
1
  import { Util } from '../util/Util';
3
2
  import { APIClan, APIClanList, APIClanMemberList, APIClanRankingList, APIClanVersusRankingList, APIClanWar, APIClanWarLeagueGroup, APIClanWarLog, APIGoldPassSeason, APILabelList, APILeague, APILeagueList, APILeagueSeasonList, APILocation, APILocationList, APIPlayer, APIPlayerRankingList, APIPlayerSeasonRankingList, APIPlayerVersusRankingList, APIVerifyToken, APIWarLeague, APIWarLeagueList, SearchOptions, ClanSearchOptions, RESTOptions, OverrideOptions, LoginOptions } from '../types';
3
+ import { RequestHandler } from './RequestHandler';
4
4
  /** Represents a REST Manager of the client. */
5
5
  export declare class RESTManager {
6
6
  /** Request Handler for the RESTManager. */
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RESTManager = void 0;
4
- const RequestHandler_1 = require("./RequestHandler");
5
4
  const Util_1 = require("../util/Util");
5
+ const RequestHandler_1 = require("./RequestHandler");
6
6
  /** Represents a REST Manager of the client. */
7
7
  class RESTManager {
8
8
  constructor(options) {
@@ -16,13 +16,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
16
16
  var _RequestHandler_keyIndex;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.RequestHandler = void 0;
19
+ const node_https_1 = __importDefault(require("node:https"));
20
+ const node_fetch_1 = __importDefault(require("node-fetch"));
19
21
  const Constants_1 = require("../util/Constants");
20
- const HTTPError_1 = require("./HTTPError");
21
22
  const Store_1 = require("../util/Store");
22
- const node_fetch_1 = __importDefault(require("node-fetch"));
23
- const https_1 = __importDefault(require("https"));
23
+ const HTTPError_1 = require("./HTTPError");
24
24
  const IP_REGEX = /\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}/g;
25
- const agent = new https_1.default.Agent({ keepAlive: true });
25
+ const agent = new node_https_1.default.Agent({ keepAlive: true });
26
26
  /** Represents a Request Handler. */
27
27
  class RequestHandler {
28
28
  constructor(options) {
@@ -1,7 +1,7 @@
1
1
  import { APIClan, OverrideOptions } from '../types';
2
+ import { Client } from '../client/Client';
2
3
  import { ChatLanguage } from './ChatLanguage';
3
4
  import { ClanMember } from './ClanMember';
4
- import { Client } from '../client/Client';
5
5
  import { WarLeague } from './WarLeague';
6
6
  import type { Player } from './Player';
7
7
  import { Location } from './Location';
@@ -1,9 +1,9 @@
1
1
  import { APIPlayer, OverrideOptions } from '../types';
2
+ import { Client } from '../client/Client';
2
3
  import { LegendStatistics } from './LegendStatistics';
3
4
  import { Achievement } from './Achievement';
4
5
  import { Hero, Spell, Troop } from './Unit';
5
6
  import { PlayerClan } from './PlayerClan';
6
- import { Client } from '../client/Client';
7
7
  import { League } from './League';
8
8
  import { Label } from './Label';
9
9
  /** Represents a Clash of Clans Player. */
@@ -1,6 +1,6 @@
1
1
  import { APIClanRanking, APIClanVersusRanking, APIPlayerRanking, APIPlayerVersusRanking } from '../types';
2
- import { PlayerClan } from './PlayerClan';
3
2
  import { Client } from '../client/Client';
3
+ import { PlayerClan } from './PlayerClan';
4
4
  import { Location } from './Location';
5
5
  import { League } from './League';
6
6
  import { Badge } from './Badge';
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -1,9 +1,9 @@
1
1
  import { QueueThrottler, BatchThrottler } from '../rest/Throttler';
2
2
  export interface Store<T = any> {
3
- set(key: string, value: T, ttl?: number): boolean | Promise<boolean>;
4
- get(key: string): T | null | Promise<T | null>;
5
- delete(key: string): boolean | Promise<boolean>;
6
- clear(): void | Promise<void>;
3
+ set: (key: string, value: T, ttl?: number) => boolean | Promise<boolean>;
4
+ get: (key: string) => T | null | Promise<T | null>;
5
+ delete: (key: string) => boolean | Promise<boolean>;
6
+ clear: () => void | Promise<void>;
7
7
  }
8
8
  /** Options for a {@link Client} */
9
9
  export interface ClientOptions {
@@ -36,6 +36,10 @@ export declare const PollingEvents: {
36
36
  readonly Error: "error";
37
37
  readonly Debug: "debug";
38
38
  };
39
+ export declare const RestEvents: {
40
+ readonly Error: "error";
41
+ readonly Debug: "debug";
42
+ };
39
43
  export declare const CWLRounds: {
40
44
  readonly PreviousRound: "warEnded";
41
45
  readonly CurrentRound: "inWar";
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.RawData = exports.CWLRounds = exports.PollingEvents = exports.FriendlyWarPreparationTimes = exports.WarLeagues = exports.Leagues = exports.LegendLeagueId = exports.UnrankedLeagueData = exports.HeroPets = exports.Heroes = exports.BuilderTroops = exports.Spells = exports.DarkElixirSpells = exports.ElixirSpells = exports.SuperTroops = exports.SiegeMachines = exports.HomeTroops = exports.DarkElixirTroops = exports.ElixirTroops = exports.DevSiteAPIBaseURL = exports.APIBaseURL = void 0;
6
+ exports.RawData = exports.CWLRounds = exports.RestEvents = exports.PollingEvents = exports.FriendlyWarPreparationTimes = exports.WarLeagues = exports.Leagues = exports.LegendLeagueId = exports.UnrankedLeagueData = exports.HeroPets = exports.Heroes = exports.BuilderTroops = exports.Spells = exports.DarkElixirSpells = exports.ElixirSpells = exports.SuperTroops = exports.SiegeMachines = exports.HomeTroops = exports.DarkElixirTroops = exports.ElixirTroops = exports.DevSiteAPIBaseURL = exports.APIBaseURL = void 0;
7
7
  const raw_json_1 = __importDefault(require("../util/raw.json"));
8
8
  exports.APIBaseURL = 'https://api.clashofclans.com/v1';
9
9
  exports.DevSiteAPIBaseURL = 'https://developer.clashofclans.com/api';
@@ -134,6 +134,10 @@ exports.PollingEvents = {
134
134
  Error: 'error',
135
135
  Debug: 'debug'
136
136
  };
137
+ exports.RestEvents = {
138
+ Error: 'error',
139
+ Debug: 'debug'
140
+ };
137
141
  exports.CWLRounds = {
138
142
  PreviousRound: 'warEnded',
139
143
  CurrentRound: 'inWar',
package/dist/util/Util.js CHANGED
@@ -114,8 +114,8 @@ class Util extends null {
114
114
  }
115
115
  /** Parse in-game army link into troops and spells count with respective Id's. */
116
116
  static parseArmyLink(link) {
117
- const unitsMatches = link.match(/u(?<units>[\d+x-]+)/);
118
- const spellsMatches = link.match(/s(?<spells>[\d+x-]+)/);
117
+ const unitsMatches = /u(?<units>[\d+x-]+)/.exec(link);
118
+ const spellsMatches = /s(?<spells>[\d+x-]+)/.exec(link);
119
119
  const unitsPart = unitsMatches?.groups?.unit?.split('-') ?? [];
120
120
  const spellParts = spellsMatches?.groups?.spells?.split('-') ?? [];
121
121
  const units = unitsPart
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clashofclans.js",
3
- "version": "3.0.0-dev.392ca4c",
3
+ "version": "3.0.0-dev.f6ce42f",
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",
@@ -34,61 +34,6 @@
34
34
  "url": "https://github.com/clashperk/clashofclans.js/issues"
35
35
  },
36
36
  "homepage": "https://clashofclans.js.org",
37
- "eslintConfig": {
38
- "extends": [
39
- "marine/prettier/node",
40
- "plugin:prettier/recommended"
41
- ],
42
- "plugins": [
43
- "prettier"
44
- ],
45
- "ignorePatterns": [
46
- "dist/*",
47
- "docs/*"
48
- ],
49
- "parser": "@typescript-eslint/parser",
50
- "rules": {
51
- "no-process-env": 0,
52
- "no-inline-comments": 0,
53
- "no-warning-comments": 0,
54
- "comma-dangle": [
55
- "error",
56
- "never"
57
- ],
58
- "@typescript-eslint/naming-convention": 0,
59
- "@typescript-eslint/no-floating-promises": 0,
60
- "@typescript-eslint/no-misused-promises": 0
61
- }
62
- },
63
- "prettier": {
64
- "semi": true,
65
- "tabWidth": 4,
66
- "printWidth": 140,
67
- "singleQuote": true,
68
- "quoteProps": "consistent",
69
- "bracketSpacing": true,
70
- "trailingComma": "none",
71
- "overrides": [
72
- {
73
- "files": [
74
- "*.md"
75
- ],
76
- "options": {
77
- "useTabs": false,
78
- "tabWidth": 4
79
- }
80
- },
81
- {
82
- "files": [
83
- "*.yml"
84
- ],
85
- "options": {
86
- "useTabs": false,
87
- "tabWidth": 2
88
- }
89
- }
90
- ]
91
- },
92
37
  "standard-version": {
93
38
  "skip": {
94
39
  "changelog": false,
@@ -100,18 +45,20 @@
100
45
  "node-fetch": "^2.6.7"
101
46
  },
102
47
  "devDependencies": {
103
- "@types/node": "^16.10.3",
104
48
  "@types/node-fetch": "^2.5.12",
49
+ "gen-esm-wrapper": "^1.1.3",
50
+ "@types/node": "^18.6.4",
105
51
  "@typescript-eslint/eslint-plugin": "^5.4.0",
106
52
  "@typescript-eslint/parser": "^5.4.0",
53
+ "dotenv": "^16.0.2",
107
54
  "eslint": "^8.3.0",
108
55
  "eslint-config-marine": "^9.0.6",
109
56
  "eslint-config-prettier": "^8.3.0",
57
+ "eslint-plugin-import": "^2.26.0",
110
58
  "eslint-plugin-prettier": "^4.0.0",
111
- "gen-esm-wrapper": "^1.1.3",
112
59
  "prettier": "^2.4.1",
113
60
  "rimraf": "^3.0.2",
114
- "typescript": "^4.5.2"
61
+ "typescript": "^4.7.4"
115
62
  },
116
63
  "engines": {
117
64
  "node": ">=14.x"
@@ -1,86 +0,0 @@
1
- import { Clan, ClanWar, Player } from '../struct';
2
- import { PollingClient } from './PollingClient';
3
- /** Represents PollingEvent Manager of the {@link PollingClient}. */
4
- export declare class PollingEventManager {
5
- private readonly pollingClient;
6
- private readonly _clanTags;
7
- private readonly _playerTags;
8
- private readonly _warTags;
9
- private readonly _clans;
10
- private readonly _players;
11
- private readonly _wars;
12
- private readonly _pollingEvents;
13
- private _inMaintenance;
14
- private _maintenanceStartTime;
15
- constructor(pollingClient: PollingClient);
16
- /** Initialize the PollingEvent Manager to start pulling the data by polling api. */
17
- init(): Promise<string[]>;
18
- /** Add clan tags to clan polling events. */
19
- addClans(tags: string[] | string): this;
20
- /** Delete clan tags from clan polling events. */
21
- deleteClans(tags: string[] | string): this;
22
- /** Add player tags for player polling events. */
23
- addPlayers(tags: string[] | string): this;
24
- /** Delete player tags from player polling events. */
25
- deletePlayers(tags: string[] | string): this;
26
- /** Add clan tags for war polling events. */
27
- addWars(tags: string[] | string): this;
28
- /** Delete clan tags from war polling events. */
29
- deleteWars(tags: string[] | string): this;
30
- /**
31
- * Set your own custom clan polling event.
32
- *
33
- * In order to emit the custom polling event, you must have this filter function that returns a boolean.
34
- *
35
- * @example
36
- * ```js
37
- * client.pollingEvents.addClans(['#2PP', '#8QU8J9LP']);
38
- *
39
- * client.pollingEvents.setClanEvent({
40
- * name: 'clanMemberUpdate',
41
- * filter: (oldClan, newClan) => {
42
- * return oldClan.memberCount !== newClan.memberCount;
43
- * }
44
- * });
45
- *
46
- * client.on('clanMemberUpdate', (oldClan, newClan) => {
47
- * console.log(oldClan.memberCount, newClan.memberCount);
48
- * });
49
- *
50
- * (async function () {
51
- * await client.pollingEvents.init();
52
- * })();
53
- * ```
54
- * @returns
55
- */
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: {
75
- name: string;
76
- filter: (oldPlayer: Player, newPlayer: Player) => boolean;
77
- }): this;
78
- private maintenanceHandler;
79
- private seasonEndHandler;
80
- private clanUpdateHandler;
81
- private playerUpdateHandler;
82
- private warUpdateHandler;
83
- private runClanUpdate;
84
- private runPlayerUpdate;
85
- private runWarUpdate;
86
- }
@@ -1,278 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PollingEventManager = void 0;
4
- const HTTPError_1 = require("../rest/HTTPError");
5
- const Constants_1 = require("../util/Constants");
6
- const Util_1 = require("../util/Util");
7
- /** Represents PollingEvent Manager of the {@link PollingClient}. */
8
- class PollingEventManager {
9
- constructor(pollingClient) {
10
- this.pollingClient = pollingClient;
11
- this._clanTags = new Set();
12
- this._playerTags = new Set();
13
- this._warTags = new Set();
14
- this._clans = new Map();
15
- this._players = new Map();
16
- this._wars = new Map();
17
- this._pollingEvents = {
18
- clans: [],
19
- wars: [],
20
- players: []
21
- };
22
- this._inMaintenance = Boolean(false);
23
- this._maintenanceStartTime = null;
24
- }
25
- /** Initialize the PollingEvent Manager to start pulling the data by polling api. */
26
- async init() {
27
- this.seasonEndHandler();
28
- this.maintenanceHandler();
29
- this.clanUpdateHandler();
30
- this.playerUpdateHandler();
31
- this.warUpdateHandler();
32
- return Promise.resolve(this.pollingClient.eventNames());
33
- }
34
- /** Add clan tags to clan polling events. */
35
- addClans(tags) {
36
- for (const tag of Array.isArray(tags) ? tags : [tags]) {
37
- this._clanTags.add(this.pollingClient.util.formatTag(tag));
38
- }
39
- return this;
40
- }
41
- /** Delete clan tags from clan polling events. */
42
- deleteClans(tags) {
43
- for (const tag of Array.isArray(tags) ? tags : [tags]) {
44
- const key = this.pollingClient.util.formatTag(tag);
45
- this._clans.delete(key);
46
- this._clanTags.delete(key);
47
- }
48
- return this;
49
- }
50
- /** Add player tags for player polling events. */
51
- addPlayers(tags) {
52
- for (const tag of Array.isArray(tags) ? tags : [tags]) {
53
- this._playerTags.add(this.pollingClient.util.formatTag(tag));
54
- }
55
- return this;
56
- }
57
- /** Delete player tags from player polling events. */
58
- deletePlayers(tags) {
59
- for (const tag of Array.isArray(tags) ? tags : [tags]) {
60
- const key = this.pollingClient.util.formatTag(tag);
61
- this._players.delete(key);
62
- this._playerTags.delete(key);
63
- }
64
- return this;
65
- }
66
- /** Add clan tags for war polling events. */
67
- addWars(tags) {
68
- for (const tag of Array.isArray(tags) ? tags : [tags]) {
69
- this._warTags.add(this.pollingClient.util.formatTag(tag));
70
- }
71
- return this;
72
- }
73
- /** Delete clan tags from war polling events. */
74
- deleteWars(tags) {
75
- for (const tag of Array.isArray(tags) ? tags : [tags]) {
76
- const key = this.pollingClient.util.formatTag(tag);
77
- this._wars.delete(`${key}:${1}`);
78
- this._wars.delete(`${key}:${2}`);
79
- this._warTags.delete(key);
80
- }
81
- return this;
82
- }
83
- /**
84
- * Set your own custom clan polling event.
85
- *
86
- * In order to emit the custom polling event, you must have this filter function that returns a boolean.
87
- *
88
- * @example
89
- * ```js
90
- * client.pollingEvents.addClans(['#2PP', '#8QU8J9LP']);
91
- *
92
- * client.pollingEvents.setClanEvent({
93
- * name: 'clanMemberUpdate',
94
- * filter: (oldClan, newClan) => {
95
- * return oldClan.memberCount !== newClan.memberCount;
96
- * }
97
- * });
98
- *
99
- * client.on('clanMemberUpdate', (oldClan, newClan) => {
100
- * console.log(oldClan.memberCount, newClan.memberCount);
101
- * });
102
- *
103
- * (async function () {
104
- * await client.pollingEvents.init();
105
- * })();
106
- * ```
107
- * @returns
108
- */
109
- setClanEvent(event) {
110
- if (!event.name)
111
- throw new Error('Event name is required.');
112
- if (typeof event.filter !== 'function')
113
- throw new Error('Filter function is required.');
114
- this._pollingEvents.clans.push(event);
115
- return this;
116
- }
117
- /**
118
- * Set your own custom war event.
119
- *
120
- * In order to emit the custom event, you must have this filter function that returns a boolean.
121
- */
122
- setWarEvent(event) {
123
- if (!event.name)
124
- throw new Error('Event name is required.');
125
- if (typeof event.filter !== 'function')
126
- throw new Error('Filter function is required.');
127
- this._pollingEvents.wars.push(event);
128
- return this;
129
- }
130
- /**
131
- * Set your own custom player event.
132
- *
133
- * In order to emit the custom event, you must have this filter function that returns a boolean.
134
- */
135
- setPlayerEvent(event) {
136
- if (!event.name)
137
- throw new Error('Event name is required.');
138
- if (typeof event.filter !== 'function')
139
- throw new Error('Filter function is required.');
140
- this._pollingEvents.players.push(event);
141
- return this;
142
- }
143
- async maintenanceHandler() {
144
- setTimeout(this.maintenanceHandler.bind(this), 10000).unref();
145
- try {
146
- const res = await this.pollingClient.rest.getClans({ maxMembers: Math.floor(Math.random() * 40) + 10, limit: 1 });
147
- if (res.status === 200 && this._inMaintenance) {
148
- this._inMaintenance = Boolean(false);
149
- const duration = Date.now() - this._maintenanceStartTime.getTime();
150
- this._maintenanceStartTime = null;
151
- this.pollingClient.emit(Constants_1.PollingEvents.MaintenanceEnd, duration);
152
- }
153
- }
154
- catch (error) {
155
- if (error instanceof HTTPError_1.HTTPError && error.status === 503 && !this._inMaintenance) {
156
- this._inMaintenance = Boolean(true);
157
- this._maintenanceStartTime = new Date();
158
- this.pollingClient.emit(Constants_1.PollingEvents.MaintenanceStart);
159
- }
160
- }
161
- }
162
- seasonEndHandler() {
163
- const end = Util_1.Util.getSeasonEndTime().getTime() - Date.now();
164
- // Why this? setTimeout can be up to 24.8 days or 2147483647ms [(2^31 - 1) Max 32bit Integer]
165
- if (end > 24 * 60 * 60 * 1000) {
166
- setTimeout(this.seasonEndHandler.bind(this), 60 * 60 * 1000);
167
- }
168
- else if (end > 0) {
169
- setTimeout(() => {
170
- this.pollingClient.emit(Constants_1.PollingEvents.NewSeasonStart, Util_1.Util.getSeasonId());
171
- }, end + 100).unref();
172
- }
173
- }
174
- async clanUpdateHandler() {
175
- this.pollingClient.emit(Constants_1.PollingEvents.ClanLoopStart);
176
- for (const tag of this._clanTags)
177
- await this.runClanUpdate(tag);
178
- this.pollingClient.emit(Constants_1.PollingEvents.ClanLoopEnd);
179
- setTimeout(this.clanUpdateHandler.bind(this), 10000);
180
- }
181
- async playerUpdateHandler() {
182
- this.pollingClient.emit(Constants_1.PollingEvents.PlayerLoopStart);
183
- for (const tag of this._playerTags)
184
- await this.runPlayerUpdate(tag);
185
- this.pollingClient.emit(Constants_1.PollingEvents.PlayerLoopEnd);
186
- setTimeout(this.playerUpdateHandler.bind(this), 10000);
187
- }
188
- async warUpdateHandler() {
189
- this.pollingClient.emit(Constants_1.PollingEvents.WarLoopStart);
190
- for (const tag of this._warTags)
191
- await this.runWarUpdate(tag);
192
- this.pollingClient.emit(Constants_1.PollingEvents.WarLoopEnd);
193
- setTimeout(this.warUpdateHandler.bind(this), 10000);
194
- }
195
- async runClanUpdate(tag) {
196
- if (this._inMaintenance)
197
- return null;
198
- const clan = await this.pollingClient.getClan(tag).catch(() => null);
199
- if (!clan)
200
- return null;
201
- const cached = this._clans.get(clan.tag);
202
- if (!cached)
203
- return this._clans.set(clan.tag, clan);
204
- for (const { name, filter } of this._pollingEvents.clans) {
205
- try {
206
- if (!(await filter(cached, clan)))
207
- continue;
208
- this.pollingClient.emit(name, cached, clan);
209
- }
210
- catch (error) {
211
- this.pollingClient.emit(Constants_1.PollingEvents.Error, error);
212
- }
213
- }
214
- return this._clans.set(clan.tag, clan);
215
- }
216
- async runPlayerUpdate(tag) {
217
- if (this._inMaintenance)
218
- return null;
219
- const player = await this.pollingClient.getPlayer(tag).catch(() => null);
220
- if (!player)
221
- return null;
222
- const cached = this._players.get(player.tag);
223
- if (!cached)
224
- return this._players.set(player.tag, player);
225
- for (const { name, filter } of this._pollingEvents.players) {
226
- try {
227
- if (!(await filter(cached, player)))
228
- continue;
229
- this.pollingClient.emit(name, cached, player);
230
- }
231
- catch (error) {
232
- this.pollingClient.emit(Constants_1.PollingEvents.Error, error);
233
- }
234
- }
235
- return this._players.set(player.tag, player);
236
- }
237
- async runWarUpdate(tag) {
238
- if (this._inMaintenance)
239
- return null;
240
- const clanWars = await this.pollingClient.getWars(tag).catch(() => null);
241
- if (!clanWars?.length)
242
- return null;
243
- clanWars.forEach(async (war, index) => {
244
- const key = `${tag}:${index}`;
245
- const cached = this._wars.get(key);
246
- if (!cached)
247
- return this._wars.set(key, war);
248
- for (const { name, filter } of this._pollingEvents.wars) {
249
- try {
250
- if (!(await filter(cached, war)))
251
- continue;
252
- this.pollingClient.emit(name, cached, war);
253
- }
254
- catch (error) {
255
- this.pollingClient.emit(Constants_1.PollingEvents.Error, error);
256
- }
257
- }
258
- // check for war end
259
- if (index === 1 && cached.warTag !== war.warTag) {
260
- const data = await this.pollingClient.getLeagueWar({ clanTag: tag, round: 'PreviousRound' }).catch(() => null);
261
- if (data && data.warTag === cached.warTag) {
262
- for (const { name, filter } of this._pollingEvents.wars) {
263
- try {
264
- if (!(await filter(cached, data)))
265
- continue;
266
- this.pollingClient.emit(name, cached, data);
267
- }
268
- catch (error) {
269
- this.pollingClient.emit(Constants_1.PollingEvents.Error, error);
270
- }
271
- }
272
- }
273
- }
274
- return this._wars.set(key, war);
275
- });
276
- }
277
- }
278
- exports.PollingEventManager = PollingEventManager;