clashofclans.js 3.4.0-dev.c71cfc8 → 3.4.2-dev.40758b1

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.
@@ -112,8 +112,8 @@ export declare class Client extends EventEmitter {
112
112
  getPlayers(playerTags: string[], options?: OverrideOptions): Promise<Player[]>;
113
113
  /** Verify Player API token that can be found from the Game settings. */
114
114
  verifyPlayerToken(playerTag: string, token: string, options?: OverrideOptions): Promise<boolean>;
115
- /** Get a list of Leagues. */
116
- getLeagues(options?: SearchOptions): Promise<import("../types").APILeague[]>;
115
+ /** Get a list of League Tiers. */
116
+ getLeaguesTiers(options?: SearchOptions): Promise<import("../types").APILeagueTier[]>;
117
117
  /** Get a list of Leagues. */
118
118
  getBuilderBaseLeagues(options?: SearchOptions): Promise<import("../types").APIBuilderBaseLeague[]>;
119
119
  /** Get a list of Capital Leagues. */
@@ -185,9 +185,9 @@ class Client extends node_events_1.EventEmitter {
185
185
  const { body } = await this.rest.verifyPlayerToken(playerTag, token, options);
186
186
  return body.status === 'ok';
187
187
  }
188
- /** Get a list of Leagues. */
189
- async getLeagues(options) {
190
- const { body } = await this.rest.getLeagues(options);
188
+ /** Get a list of League Tiers. */
189
+ async getLeaguesTiers(options) {
190
+ const { body } = await this.rest.getLeagueTiers(options);
191
191
  return body.items;
192
192
  }
193
193
  /** Get a list of Leagues. */
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { EventEmitter } from 'node:events';
3
- import { APIBuilderBaseLeague, APIBuilderBaseLeagueList, APICapitalLeague, APICapitalLeagueList, APICapitalRaidSeasons, APIClan, APIClanBuilderBaseRankingList, APIClanCapitalRankingList, APIClanList, APIClanMemberList, APIClanRankingList, APIClanWar, APIClanWarLeagueGroup, APIClanWarLogList, APIGoldPassSeason, APILabelList, APILeague, APILeagueList, APILeagueSeasonList, APILocation, APILocationList, APIPlayer, APIPlayerBuilderBaseRankingList, APIPlayerRankingList, APIPlayerSeasonRankingList, APIVerifyToken, APIWarLeague, APIWarLeagueList, ClanSearchOptions, LoginOptions, OverrideOptions, RESTOptions, SearchOptions } from '../types';
3
+ import { APIBuilderBaseLeague, APIBuilderBaseLeagueList, APICapitalLeague, APICapitalLeagueList, APICapitalRaidSeasons, APIClan, APIClanBuilderBaseRankingList, APIClanCapitalRankingList, APIClanList, APIClanMemberList, APIClanRankingList, APIClanWar, APIClanWarLeagueGroup, APIClanWarLogList, APIGoldPassSeason, APILabelList, APILeagueSeasonList, APILeagueTier, APILeagueTierList, APILocation, APILocationList, APIPlayer, APIPlayerBuilderBaseRankingList, APIPlayerRankingList, APIPlayerSeasonRankingList, APIVerifyToken, APIWarLeague, APIWarLeagueList, ClanSearchOptions, LoginOptions, OverrideOptions, RESTOptions, SearchOptions } from '../types';
4
4
  import { RestEvents } from '../util/Constants';
5
5
  import { Util } from '../util/Util';
6
6
  import { RequestHandler } from './RequestHandler';
@@ -72,10 +72,10 @@ export declare class RESTManager extends EventEmitter {
72
72
  getPlayer(playerTag: string, options?: OverrideOptions): Promise<import("../types").Result<APIPlayer>>;
73
73
  /** Verify Player API token that can be found from the Game settings. */
74
74
  verifyPlayerToken(playerTag: string, token: string, options?: OverrideOptions): Promise<import("../types").Result<APIVerifyToken>>;
75
- /** Get a list of Leagues. */
76
- getLeagues(options?: SearchOptions): Promise<import("../types").Result<APILeagueList>>;
77
- /** Get a League info. */
78
- getLeague(leagueId: string | number, options?: OverrideOptions): Promise<import("../types").Result<APILeague>>;
75
+ /** Get a list of League Tiers. */
76
+ getLeagueTiers(options?: SearchOptions): Promise<import("../types").Result<APILeagueTierList>>;
77
+ /** Get a League tier info. */
78
+ getLeagueTier(leagueId: string | number, options?: OverrideOptions): Promise<import("../types").Result<APILeagueTier>>;
79
79
  /** Get a list of builder base leagues. */
80
80
  getBuilderBaseLeagues(options?: SearchOptions): Promise<import("../types").Result<APIBuilderBaseLeagueList>>;
81
81
  /** Get a builder base league info. */
@@ -81,14 +81,14 @@ class RESTManager extends node_events_1.EventEmitter {
81
81
  ...options
82
82
  });
83
83
  }
84
- /** Get a list of Leagues. */
85
- getLeagues(options) {
84
+ /** Get a list of League Tiers. */
85
+ getLeagueTiers(options) {
86
86
  const query = Util_1.Util.queryString(options);
87
- return this.requestHandler.request(`/leagues${query}`, options);
87
+ return this.requestHandler.request(`/leaguetiers${query}`, options);
88
88
  }
89
- /** Get a League info. */
90
- getLeague(leagueId, options) {
91
- return this.requestHandler.request(`/leagues/${leagueId}`, options);
89
+ /** Get a League tier info. */
90
+ getLeagueTier(leagueId, options) {
91
+ return this.requestHandler.request(`/leaguetiers/${leagueId}`, options);
92
92
  }
93
93
  /** Get a list of builder base leagues. */
94
94
  getBuilderBaseLeagues(options) {
@@ -1,5 +1,5 @@
1
1
  import { Client } from '../client/Client';
2
- import { APIClanMember, APILeague, APIPlayerHouse, OverrideOptions } from '../types';
2
+ import { APIClanMember, APILeagueTier, APIPlayerHouse, OverrideOptions } from '../types';
3
3
  import { League } from './League';
4
4
  export declare class ClanMember {
5
5
  /** The member's name. */
@@ -15,7 +15,7 @@ export declare class ClanMember {
15
15
  /** The member's current League. */
16
16
  league: League;
17
17
  /** The member's current Builder Base League. */
18
- builderBaseLeague: Omit<APILeague, 'iconUrls'> | null;
18
+ builderBaseLeague: Omit<APILeagueTier, 'iconUrls'> | null;
19
19
  /** The member's trophy count. */
20
20
  trophies: number;
21
21
  /** The member's builder base trophy count. */
@@ -19,7 +19,7 @@ class ClanMember {
19
19
  this.role = data.role.replace('admin', 'elder');
20
20
  this.expLevel = data.expLevel;
21
21
  // eslint-disable-next-line
22
- this.league = new League_1.League(data.league ?? Constants_1.UnrankedLeagueData);
22
+ this.league = new League_1.League(data.leagueTier ?? Constants_1.UnrankedLeagueData);
23
23
  this.trophies = data.trophies;
24
24
  this.builderBaseTrophies = data.builderBaseTrophies ?? null;
25
25
  this.clanRank = data.clanRank;
@@ -1,4 +1,4 @@
1
- import { APILeague } from '../types';
1
+ import { APILeagueTier } from '../types';
2
2
  import { Icon } from './Icon';
3
3
  /** Represents a Player's League. */
4
4
  export declare class League {
@@ -8,7 +8,7 @@ export declare class League {
8
8
  name: string;
9
9
  /** The League Icon. */
10
10
  icon: Icon;
11
- constructor(data: APILeague);
11
+ constructor(data: APILeagueTier);
12
12
  /** Position of this League. Starting from 0 (Un-ranked) */
13
13
  get position(): number;
14
14
  }
@@ -39,7 +39,7 @@ class Player {
39
39
  this.role = data.role?.replace('admin', 'elder') ?? null;
40
40
  this.warOptedIn = data.warPreference ? data.warPreference === 'in' : null;
41
41
  this.clan = data.clan ? new PlayerClan_1.PlayerClan(client, data.clan) : null;
42
- this.league = new League_1.League(data.league ?? Constants_1.UnrankedLeagueData);
42
+ this.league = new League_1.League(data.leagueTier ?? Constants_1.UnrankedLeagueData);
43
43
  this.legendStatistics = data.legendStatistics ? new LegendStatistics_1.LegendStatistics(data.legendStatistics) : null;
44
44
  this.achievements = data.achievements.map((data) => new Achievement_1.Achievement(data));
45
45
  this.labels = data.labels.map((data) => new Label_1.Label(data));
@@ -12,6 +12,10 @@ export interface APIIcon {
12
12
  /** Medium Icon is not available for Unranked Icon. */
13
13
  medium?: string;
14
14
  }
15
+ export interface APILeagueTierIcon {
16
+ small: string;
17
+ large: string;
18
+ }
15
19
  export interface APIBadge {
16
20
  small: string;
17
21
  large: string;
@@ -68,8 +72,8 @@ export interface APIClanMember {
68
72
  role: 'member' | 'admin' | 'coLeader' | 'leader';
69
73
  expLevel: number;
70
74
  townHallLevel: number;
71
- league: APILeague;
72
- builderBaseLeague?: Omit<APILeague, 'iconUrls'>;
75
+ leagueTier?: APILeagueTier;
76
+ builderBaseLeague?: Omit<APILeagueTier, 'iconUrls'>;
73
77
  trophies: number;
74
78
  builderBaseTrophies?: number;
75
79
  clanRank: number;
@@ -262,7 +266,7 @@ export interface APIPlayer {
262
266
  role?: string;
263
267
  warPreference?: 'in' | 'out';
264
268
  clan?: APIPlayerClan;
265
- league?: APILeague;
269
+ leagueTier?: APILeagueTier;
266
270
  builderBaseLeague?: {
267
271
  id: number;
268
272
  name: string;
@@ -357,7 +361,7 @@ export interface APIPlayerRanking {
357
361
  rank: number;
358
362
  previousRank: number;
359
363
  clan?: Omit<APIPlayerClan, 'clanLevel'>;
360
- league: APILeague;
364
+ leagueTier?: APILeagueTier;
361
365
  }
362
366
  /** /locations/{locationId}/rankings/clans-builder-base */
363
367
  export interface APIClanBuilderBaseRankingList {
@@ -409,9 +413,9 @@ export interface APIClanCapitalRankingList {
409
413
  items: APIClanCapitalRanking[];
410
414
  paging: APIPaging;
411
415
  }
412
- /** /leagues */
413
- export interface APILeagueList {
414
- items: APILeague[];
416
+ /** /leaguetiers */
417
+ export interface APILeagueTierList {
418
+ items: APILeagueTier[];
415
419
  paging: APIPaging;
416
420
  }
417
421
  export interface APIBuilderBaseLeagueList {
@@ -422,15 +426,14 @@ export interface APIBuilderBaseLeague {
422
426
  id: number;
423
427
  name: string;
424
428
  }
425
- /** /leagues/{leagueId} */
426
- export interface APILeague {
429
+ export interface APILeagueTier {
427
430
  id: number;
428
431
  name: string;
429
- iconUrls: APIIcon;
432
+ iconUrls: APILeagueTierIcon;
430
433
  }
431
434
  /** /leagues/{leagueId}/seasons/{seasonId} */
432
435
  export interface APIPlayerSeasonRankingList {
433
- items: Omit<APIPlayerRanking, 'league'>[];
436
+ items: Omit<APIPlayerRanking, 'leagueTier'>[];
434
437
  paging: APIPaging;
435
438
  }
436
439
  /** /leagues/{leagueId}/seasons */
@@ -17,7 +17,7 @@ export declare const UnrankedLeagueData: {
17
17
  name: string;
18
18
  iconUrls: {
19
19
  small: string;
20
- tiny: string;
20
+ large: string;
21
21
  };
22
22
  };
23
23
  export declare const UnrankedLeagueId = 29000000;
@@ -20,11 +20,11 @@ exports.BuilderTroops = raw_json_1.default.RAW_UNITS.filter((unit) => unit.subCa
20
20
  exports.Heroes = raw_json_1.default.RAW_UNITS.filter((unit) => unit.subCategory === 'hero').map((unit) => unit.name);
21
21
  exports.HeroPets = raw_json_1.default.RAW_UNITS.filter((unit) => unit.subCategory === 'pet').map((unit) => unit.name);
22
22
  exports.UnrankedLeagueData = {
23
- id: 29000000,
23
+ id: 105000000,
24
24
  name: 'Unranked',
25
25
  iconUrls: {
26
- small: 'https://api-assets.clashofclans.com/leagues/72/e--YMyIexEQQhE4imLoJcwhYn6Uy8KqlgyY3_kFV6t4.png',
27
- tiny: 'https://api-assets.clashofclans.com/leagues/36/e--YMyIexEQQhE4imLoJcwhYn6Uy8KqlgyY3_kFV6t4.png'
26
+ small: 'https://api-assets.clashofclans.com/leaguetiers/125/yyYo5DUFeFBZvmMEQh0ZxvG-1sUOZ_S3kDMB7RllXX0.png',
27
+ large: 'https://api-assets.clashofclans.com/leaguetiers/326/yyYo5DUFeFBZvmMEQh0ZxvG-1sUOZ_S3kDMB7RllXX0.png'
28
28
  }
29
29
  };
30
30
  exports.UnrankedLeagueId = 29000000;
@@ -33,7 +33,9 @@ export declare class Util extends null {
33
33
  static formatDate(date: string): Date;
34
34
  /** Returns a string containing a query string suitable for use in a URL. */
35
35
  static queryString(options?: SearchOptions | ClanSearchOptions): string;
36
+ /** @deprecated Use getSeason instead */
36
37
  static getSeasonStart(inputDate: Date): Date;
38
+ /** @deprecated Use getSeason instead */
37
39
  static getSeasonEnd(inputDate: Date, forward?: boolean): Date;
38
40
  /** Get the current season ID. */
39
41
  static getSeasonId(): string;
@@ -45,6 +47,7 @@ export declare class Util extends null {
45
47
  static getSeason(timestamp?: Date, forward?: boolean): {
46
48
  endTime: Date;
47
49
  startTime: Date;
50
+ seasonId: string;
48
51
  };
49
52
  static allSettled<T>(values: Promise<T>[]): Promise<T[]>;
50
53
  static delay(ms: number): Promise<unknown>;
package/dist/util/Util.js CHANGED
@@ -95,6 +95,7 @@ class Util extends null {
95
95
  const query = new URLSearchParams(Object.entries(options).filter(([key]) => params.includes(key))).toString();
96
96
  return query.length ? `?${query}` : query;
97
97
  }
98
+ /** @deprecated Use getSeason instead */
98
99
  static getSeasonStart(inputDate) {
99
100
  const lastMonthLastDay = new Date(Date.UTC(inputDate.getUTCFullYear(), inputDate.getUTCMonth(), 0));
100
101
  const lastMonthLastMonday = new Date(lastMonthLastDay);
@@ -102,6 +103,7 @@ class Util extends null {
102
103
  lastMonthLastMonday.setUTCHours(5, 0, 0, 0);
103
104
  return lastMonthLastMonday;
104
105
  }
106
+ /** @deprecated Use getSeason instead */
105
107
  static getSeasonEnd(inputDate, forward = true) {
106
108
  const lastDayOfMonth = new Date(Date.UTC(inputDate.getUTCFullYear(), inputDate.getUTCMonth() + 1, 0));
107
109
  const lastMonday = new Date(lastDayOfMonth);
@@ -121,7 +123,7 @@ class Util extends null {
121
123
  }
122
124
  /** Get the current season ID. */
123
125
  static getSeasonId() {
124
- return this.getSeasonEnd(new Date()).toISOString().slice(0, 7);
126
+ return this.getSeason(new Date()).seasonId;
125
127
  }
126
128
  /**
127
129
  * Get the season start and end timestamp.
@@ -129,9 +131,34 @@ class Util extends null {
129
131
  * @param {boolean} forward - Whether to forward to the next month if the returned date is in the past relative to the given timestamp. Defaults to true.
130
132
  */
131
133
  static getSeason(timestamp, forward = true) {
132
- const endTime = this.getSeasonEnd(timestamp ?? new Date(), forward);
133
- const startTime = this.getSeasonStart(endTime);
134
- return { endTime, startTime };
134
+ const target = timestamp ?? new Date();
135
+ if (target < new Date('2025-08-25T05:00:00.000Z')) {
136
+ const endTime = this.getSeasonEnd(timestamp ?? new Date(), forward);
137
+ const startTime = this.getSeasonStart(endTime);
138
+ return { endTime, startTime, seasonId: endTime.toISOString().slice(0, 7) };
139
+ }
140
+ if (target > new Date('2025-08-25T05:00:00.000Z') && target <= new Date('2025-10-06T05:00:00.000Z')) {
141
+ return {
142
+ startTime: new Date('2025-08-25T05:00:00.000Z'),
143
+ endTime: new Date('2025-10-06T05:00:00.000Z'),
144
+ seasonId: '2025-09'
145
+ };
146
+ }
147
+ // After 6th October 2025, season ends every 4 weeks
148
+ const seasonDuration = 7 * 4 * 24 * 60 * 60 * 1000;
149
+ const referenceDate = new Date('2025-10-06T05:00:00.000Z');
150
+ const timeDifference = target.getTime() - referenceDate.getTime();
151
+ const seasonsPassed = Math.floor(timeDifference / seasonDuration);
152
+ const startTime = new Date(referenceDate.getTime() + seasonsPassed * seasonDuration);
153
+ const endTime = new Date(startTime.getTime() + seasonDuration);
154
+ // "month" increments by 1 each season starting from referenceDate's month
155
+ const refYear = referenceDate.getUTCFullYear();
156
+ const refMonthIndex = referenceDate.getUTCMonth(); // 0-based (Oct -> 9)
157
+ const totalMonths = refYear * 12 + refMonthIndex + seasonsPassed;
158
+ const year = Math.floor(totalMonths / 12);
159
+ const month = totalMonths - year * 12 + 1; // 1..12
160
+ const seasonId = `${year}-${String(month).padStart(2, '0')}`;
161
+ return { startTime, endTime, seasonId };
135
162
  }
136
163
  static async allSettled(values) {
137
164
  return (await Promise.allSettled(values))
package/dist/util.spec.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ process.env.TZ = 'UTC';
3
4
  const Util_1 = require("./util/Util");
4
5
  describe('util', () => {
5
6
  it('should always be UTC', () => {
@@ -31,10 +32,56 @@ describe('util', () => {
31
32
  });
32
33
  it('should forward to the next year at the end of the year', async () => {
33
34
  const timestamp = new Date('2024-12-30T05:01');
34
- const { endTime, startTime } = Util_1.Util.getSeason(timestamp);
35
- const expectedEndTime = new Date('2025-01-27T05:00').toISOString();
35
+ const { endTime, startTime, seasonId } = Util_1.Util.getSeason(timestamp);
36
36
  const expectedStartTime = new Date('2024-12-30T05:00').toISOString();
37
+ const expectedEndTime = new Date('2025-01-27T05:00').toISOString();
38
+ expect(endTime.toISOString()).toBe(expectedEndTime);
39
+ expect(startTime.toISOString()).toBe(expectedStartTime);
40
+ expect(seasonId).toBe('2025-01');
41
+ });
42
+ it('should pass October 2025', async () => {
43
+ const timestamp = new Date('2025-10-20T05:00');
44
+ const { endTime, startTime, seasonId } = Util_1.Util.getSeason(timestamp);
45
+ const expectedEndTime = new Date('2025-11-03T05:00').toISOString();
46
+ const expectedStartTime = new Date('2025-10-06T05:00').toISOString();
47
+ expect(endTime.toISOString()).toBe(expectedEndTime);
48
+ expect(startTime.toISOString()).toBe(expectedStartTime);
49
+ expect(seasonId).toBe('2025-10');
50
+ });
51
+ it('should pass November 2025', async () => {
52
+ const timestamp = new Date('2025-11-20T05:00');
53
+ const { endTime, startTime, seasonId } = Util_1.Util.getSeason(timestamp);
54
+ const expectedStartTime = new Date('2025-11-03T05:00').toISOString();
55
+ const expectedEndTime = new Date('2025-12-01T05:00').toISOString();
56
+ expect(startTime.toISOString()).toBe(expectedStartTime);
37
57
  expect(endTime.toISOString()).toBe(expectedEndTime);
58
+ expect(seasonId).toBe('2025-11');
59
+ });
60
+ it('should pass December 2025', async () => {
61
+ const timestamp = new Date('2025-12-01T05:00');
62
+ const { endTime, startTime, seasonId } = Util_1.Util.getSeason(timestamp);
63
+ const expectedStartTime = new Date('2025-12-01T05:00').toISOString();
64
+ const expectedEndTime = new Date('2025-12-29T05:00').toISOString();
65
+ expect(startTime.toISOString()).toBe(expectedStartTime);
66
+ expect(endTime.toISOString()).toBe(expectedEndTime);
67
+ expect(seasonId).toBe('2025-12');
68
+ });
69
+ it('should pass Jan 2026', async () => {
70
+ const timestamp = new Date('2026-01-01T05:00');
71
+ const { endTime, startTime, seasonId } = Util_1.Util.getSeason(timestamp);
72
+ const expectedStartTime = new Date('2025-12-29T05:00').toISOString();
73
+ const expectedEndTime = new Date('2026-01-26T05:00').toISOString();
38
74
  expect(startTime.toISOString()).toBe(expectedStartTime);
75
+ expect(endTime.toISOString()).toBe(expectedEndTime);
76
+ expect(seasonId).toBe('2026-01');
77
+ });
78
+ it('should pass Feb 2026', async () => {
79
+ const timestamp = new Date('2026-01-27T05:00');
80
+ const { endTime, startTime, seasonId } = Util_1.Util.getSeason(timestamp);
81
+ const expectedStartTime = new Date('2026-01-26T05:00').toISOString();
82
+ const expectedEndTime = new Date('2026-02-23T05:00').toISOString();
83
+ expect(startTime.toISOString()).toBe(expectedStartTime);
84
+ expect(endTime.toISOString()).toBe(expectedEndTime);
85
+ expect(seasonId).toBe('2026-02');
39
86
  });
40
87
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clashofclans.js",
3
- "version": "3.4.0-dev.c71cfc8",
3
+ "version": "3.4.2-dev.40758b1",
4
4
  "description": "JavaScript library for interacting with the Clash of Clans API",
5
5
  "author": "https://clashofclans.js.org",
6
6
  "license": "MIT",