From b04aa16d31751b3e5c4baa9ac61a418fbd22ed8b Mon Sep 17 00:00:00 2001 From: Putro <29204244+Pewtro@users.noreply.github.com> Date: Sun, 22 Sep 2024 23:14:37 +0200 Subject: [PATCH] [SC2] Initial release of @blizzard-api/sc2 --- .changeset/empty-nails-design.md | 5 + packages/client/package.json | 1 + packages/client/src/tests/sc2.test.ts | 72 +++++++ packages/sc2/CHANGELOG.md | 1 + packages/sc2/package.json | 51 +++++ packages/sc2/src/account/account.test.ts | 12 ++ packages/sc2/src/account/account.ts | 7 + packages/sc2/src/base.ts | 21 ++ packages/sc2/src/index.ts | 45 +++++ packages/sc2/src/ladder/ladder.test.ts | 29 +++ packages/sc2/src/ladder/ladder.ts | 15 ++ packages/sc2/src/ladder/types.ts | 30 +++ packages/sc2/src/league/league.test.ts | 236 +++++++++++++++++++++++ packages/sc2/src/league/league.ts | 44 +++++ packages/sc2/src/league/types.ts | 43 +++++ packages/sc2/src/legacy/legacy.test.ts | 41 ++++ packages/sc2/src/legacy/legacy.ts | 57 ++++++ packages/sc2/src/legacy/types.ts | 142 ++++++++++++++ packages/sc2/src/profile/profile.test.ts | 37 ++++ packages/sc2/src/profile/profile.ts | 42 ++++ packages/sc2/src/profile/types.ts | 75 +++++++ pnpm-lock.yaml | 9 + 22 files changed, 1015 insertions(+) create mode 100644 .changeset/empty-nails-design.md create mode 100644 packages/client/src/tests/sc2.test.ts create mode 100644 packages/sc2/CHANGELOG.md create mode 100644 packages/sc2/package.json create mode 100644 packages/sc2/src/account/account.test.ts create mode 100644 packages/sc2/src/account/account.ts create mode 100644 packages/sc2/src/base.ts create mode 100644 packages/sc2/src/index.ts create mode 100644 packages/sc2/src/ladder/ladder.test.ts create mode 100644 packages/sc2/src/ladder/ladder.ts create mode 100644 packages/sc2/src/ladder/types.ts create mode 100644 packages/sc2/src/league/league.test.ts create mode 100644 packages/sc2/src/league/league.ts create mode 100644 packages/sc2/src/league/types.ts create mode 100644 packages/sc2/src/legacy/legacy.test.ts create mode 100644 packages/sc2/src/legacy/legacy.ts create mode 100644 packages/sc2/src/legacy/types.ts create mode 100644 packages/sc2/src/profile/profile.test.ts create mode 100644 packages/sc2/src/profile/profile.ts create mode 100644 packages/sc2/src/profile/types.ts diff --git a/.changeset/empty-nails-design.md b/.changeset/empty-nails-design.md new file mode 100644 index 0000000..232b993 --- /dev/null +++ b/.changeset/empty-nails-design.md @@ -0,0 +1,5 @@ +--- +'@blizzard-api/sc2': minor +--- + +Initial release of @blizzard-api/sc2 diff --git a/packages/client/package.json b/packages/client/package.json index 1285681..67d4c05 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -53,6 +53,7 @@ "@blizzard-api/classic-wow": "workspace:*", "@blizzard-api/core": "workspace:*", "@blizzard-api/d3": "workspace:*", + "@blizzard-api/sc2": "workspace:*", "@blizzard-api/wow": "workspace:*" }, "scripts": { diff --git a/packages/client/src/tests/sc2.test.ts b/packages/client/src/tests/sc2.test.ts new file mode 100644 index 0000000..f1c6861 --- /dev/null +++ b/packages/client/src/tests/sc2.test.ts @@ -0,0 +1,72 @@ +import { sc2 } from '@blizzard-api/sc2'; +import { describe, expect, it } from 'vitest'; +import { environment } from '../../../../environment'; +import { createBlizzardApiClient } from '../client/create-client'; + +describe.concurrent('smoketest some sc2 api responses', async () => { + const client = await createBlizzardApiClient({ + key: environment.blizzardClientId, + origin: 'eu', + secret: environment.blizzardClientSecret, + }); + it('should get player data', async () => { + const result = await client.sendRequest(sc2.player('235782')); + expect(result.data).toBeDefined(); + }); + it('should get grandmaster leaderboard', async () => { + const result = await client.sendRequest(sc2.grandmasterLeaderboard('eu')); + expect(result.data).toBeDefined(); + }); + it('should get season data', async () => { + const result = await client.sendRequest(sc2.season('eu')); + expect(result.data).toBeDefined(); + }); + it('should get league data', async () => { + const result = await client.sendRequest(sc2.getLeagueData('42', 'lotv-1v1', 'arranged', 'grandmaster')); + expect(result.data).toBeDefined(); + }); + it('should get legacy achievements', async () => { + const result = await client.sendRequest(sc2.legacyAchievements('eu')); + expect(result.data).toBeDefined(); + }); + it('should get legacy ladder', async () => { + const result = await client.sendRequest(sc2.legacyLadder('eu', 235_782)); + expect(result.data).toBeDefined(); + }); + it('should get legacy ladders', async () => { + const result = await client.sendRequest(sc2.legacyLadders('eu', 1, 235_782)); + expect(result.data).toBeDefined(); + }); + it('should get legacy match history', async () => { + const result = await client.sendRequest(sc2.legacyMatchHistory('eu', 1, 235_782)); + expect(result.data).toBeDefined(); + }); + it('should get legacy profile', async () => { + const result = await client.sendRequest(sc2.legacyProfile('eu', 1, 235_782)); + expect(result.data).toBeDefined(); + }); + it('should get legacy rewards', async () => { + const result = await client.sendRequest(sc2.legacyRewards('eu')); + expect(result.data).toBeDefined(); + }); + it('should get ladder data', async () => { + const result = await client.sendRequest(sc2.ladder('eu', 1, 235_782, 131_418_961)); + expect(result.data).toBeDefined(); + }); + it('should get ladder summary', async () => { + const result = await client.sendRequest(sc2.ladderSummary('eu', 1, 235_782)); + expect(result.data).toBeDefined(); + }); + it('should get metadata', async () => { + const result = await client.sendRequest(sc2.metadata('eu', 1, 235_782)); + expect(result.data).toBeDefined(); + }); + it('should get profile', async () => { + const result = await client.sendRequest(sc2.profile('eu', 1, 235_782)); + expect(result.data).toBeDefined(); + }); + it('should get static profile', async () => { + const result = await client.sendRequest(sc2.staticProfile('eu')); + expect(result.data).toBeDefined(); + }); +}); diff --git a/packages/sc2/CHANGELOG.md b/packages/sc2/CHANGELOG.md new file mode 100644 index 0000000..715bff3 --- /dev/null +++ b/packages/sc2/CHANGELOG.md @@ -0,0 +1 @@ +# @blizzard-api/sc2 diff --git a/packages/sc2/package.json b/packages/sc2/package.json new file mode 100644 index 0000000..fad86fd --- /dev/null +++ b/packages/sc2/package.json @@ -0,0 +1,51 @@ +{ + "name": "@blizzard-api/sc2", + "version": "0.0.0", + "license": "MIT", + "author": "Putro", + "description": "A series of helpers to interact with the Starcraft II Blizzard API", + "repository": "https://github.com/Pewtro/blizzard-api/tree/main/packages/sc2", + "type": "module", + "engines": { + "node": "^18.18 || ^20.9 || ^21.1 || ^22" + }, + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "files": [ + "dist" + ], + "keywords": [ + "blizzard", + "battlenet", + "battle.net", + "bnet", + "api", + "starcraft", + "sc2" + ], + "dependencies": {}, + "peerDependencies": { + "@blizzard-api/core": "1.2.0" + }, + "devDependencies": { + "@blizzard-api/core": "workspace:*" + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "test": "vitest run", + "test:coverage": "pnpm test -- --coverage", + "test:watch": "vitest watch" + } +} diff --git a/packages/sc2/src/account/account.test.ts b/packages/sc2/src/account/account.test.ts new file mode 100644 index 0000000..4d92592 --- /dev/null +++ b/packages/sc2/src/account/account.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, it } from 'vitest'; +import { player } from './account'; + +describe('account', () => { + it('should return the correct resource path for a given accountId', () => { + const accountId = '12345'; + const result = player(accountId); + expect(result).toEqual({ + path: `/sc2/player/12345`, + }); + }); +}); diff --git a/packages/sc2/src/account/account.ts b/packages/sc2/src/account/account.ts new file mode 100644 index 0000000..1152a08 --- /dev/null +++ b/packages/sc2/src/account/account.ts @@ -0,0 +1,7 @@ +import type { Resource } from '@blizzard-api/core'; + +export function player(accountId: string): Resource> { + return { + path: `/sc2/player/${accountId}`, + }; +} diff --git a/packages/sc2/src/base.ts b/packages/sc2/src/base.ts new file mode 100644 index 0000000..d67d25d --- /dev/null +++ b/packages/sc2/src/base.ts @@ -0,0 +1,21 @@ +/** + * Base interface for Blizzard API responses. + */ +export interface ResponseBase { + _links: { + self: { + href: string; + }; + }; +} + +//The region (1=US, 2=EU, 3=KO and TW, 5=CN). +export type StarcraftRegion = 'cn' | 'eu' | 'kr' | 'tw' | 'us'; + +export const starcraftRegion: Record = { + cn: 5, + eu: 2, + kr: 3, + tw: 3, + us: 1, +}; diff --git a/packages/sc2/src/index.ts b/packages/sc2/src/index.ts new file mode 100644 index 0000000..47e3bae --- /dev/null +++ b/packages/sc2/src/index.ts @@ -0,0 +1,45 @@ +import { player } from './account/account'; +import { grandmasterLeaderboard, season } from './ladder/ladder'; +import { getLeagueData } from './league/league'; +import { + legacyAchievements, + legacyLadder, + legacyLadders, + legacyMatchHistory, + legacyProfile, + legacyRewards, +} from './legacy/legacy'; +import { ladder, ladderSummary, metadata, profile, staticProfile } from './profile/profile'; + +export const sc2 = { + //Account + player, + //Ladder + grandmasterLeaderboard, + season, + //League + getLeagueData, + //Legacy + legacyAchievements, + legacyLadder, + legacyLadders, + legacyMatchHistory, + legacyProfile, + legacyRewards, + //Profile + ladder, + ladderSummary, + metadata, + profile, + staticProfile, +}; + +//Account +//Ladder +export type * from './ladder/types'; +//League +export type * from './league/types'; +//Legacy +export type * from './legacy/types'; +//Profile +export type * from './profile/types'; diff --git a/packages/sc2/src/ladder/ladder.test.ts b/packages/sc2/src/ladder/ladder.test.ts new file mode 100644 index 0000000..6d337b4 --- /dev/null +++ b/packages/sc2/src/ladder/ladder.test.ts @@ -0,0 +1,29 @@ +import type { Resource } from '@blizzard-api/core'; +import { describe, expect, it } from 'vitest'; +import type { StarcraftRegion } from '../base'; +import { grandmasterLeaderboard, season } from './ladder'; +import type { GrandmasterLeaderboardResponse, SeasonResponse } from './types'; + +describe('ladder', () => { + describe('grandmasterLeaderboard', () => { + it('should return the correct path for a given region', () => { + const regionId: StarcraftRegion = 'us'; + const expected: Resource = { + path: `/sc2/ladder/grandmaster/1`, + }; + const result = grandmasterLeaderboard(regionId); + expect(result).toEqual(expected); + }); + }); + + describe('season', () => { + it('should return the correct path for a given region', () => { + const regionId: StarcraftRegion = 'eu'; + const expected: Resource = { + path: `/sc2/ladder/season/2`, + }; + const result = season(regionId); + expect(result).toEqual(expected); + }); + }); +}); diff --git a/packages/sc2/src/ladder/ladder.ts b/packages/sc2/src/ladder/ladder.ts new file mode 100644 index 0000000..33a4dbd --- /dev/null +++ b/packages/sc2/src/ladder/ladder.ts @@ -0,0 +1,15 @@ +import type { Resource } from '@blizzard-api/core'; +import { starcraftRegion, type StarcraftRegion } from '../base'; +import type { GrandmasterLeaderboardResponse, SeasonResponse } from './types'; + +export function grandmasterLeaderboard(regionId: StarcraftRegion): Resource { + return { + path: `/sc2/ladder/grandmaster/${starcraftRegion[regionId]}`, + }; +} + +export function season(regionId: StarcraftRegion): Resource { + return { + path: `/sc2/ladder/season/${starcraftRegion[regionId]}`, + }; +} diff --git a/packages/sc2/src/ladder/types.ts b/packages/sc2/src/ladder/types.ts new file mode 100644 index 0000000..343607e --- /dev/null +++ b/packages/sc2/src/ladder/types.ts @@ -0,0 +1,30 @@ +export interface GrandmasterLeaderboardResponse { + ladderTeams: Array; +} + +interface LadderTeam { + joinTimestamp: number; + losses: number; + mmr: number; + points: number; + previousRank: number; + teamMembers: Array; + wins: number; +} + +interface TeamMember { + clanTag?: string; + displayName: string; + favoriteRace: 'protoss' | 'random' | 'terran' | 'zerg'; + id: string; + realm: number; + region: number; +} + +export interface SeasonResponse { + endDate: string; + number: number; + seasonId: number; + startDate: string; + year: number; +} diff --git a/packages/sc2/src/league/league.test.ts b/packages/sc2/src/league/league.test.ts new file mode 100644 index 0000000..4dbdbfc --- /dev/null +++ b/packages/sc2/src/league/league.test.ts @@ -0,0 +1,236 @@ +import { describe, expect, it } from 'vitest'; +import { getLeagueData } from './league'; +import type { StarcraftLeagueId, StarcraftLeagueQueue, StarcraftLeagueTeamType } from './types'; + +describe('league', () => { + it('should return the correct path for grandmaster league', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-1v1'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'grandmaster'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/201/0/6'); + }); + + it('should return the correct path for bronze league', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-1v1'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'bronze'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/201/0/0'); + }); + + it('should return the correct path for silver league', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-1v1'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'silver'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/201/0/1'); + }); + + it('should return the correct path for gold league', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-1v1'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'gold'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/201/0/2'); + }); + + it('should return the correct path for platinum league', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-1v1'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'platinum'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/201/0/3'); + }); + + it('should return the correct path for diamond league', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-1v1'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'diamond'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/201/0/4'); + }); + + it('should return the correct path for master league', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-1v1'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/201/0/5'); + }); + + it('should return the correct path for hots-1v1 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'hots-1v1'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/101/0/5'); + }); + + it('should return the correct path for hots-2v2 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'hots-2v2'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/102/0/5'); + }); + + it('should return the correct path for hots-3v3 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'hots-3v3'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/103/0/5'); + }); + + it('should return the correct path for hots-4v4 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'hots-4v4'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/104/0/5'); + }); + + it('should return the correct path for lotv-2v2 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-2v2'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/202/0/5'); + }); + + it('should return the correct path for lotv-3v3 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-3v3'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/203/0/5'); + }); + + it('should return the correct path for lotv-4v4 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-4v4'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/204/0/5'); + }); + + it('should return the correct path for lotv-archon queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'lotv-archon'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/206/0/5'); + }); + + it('should return the correct path for wol-1v1 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'wol-1v1'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/1/0/5'); + }); + + it('should return the correct path for wol-2v2 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'wol-2v2'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/2/0/5'); + }); + + it('should return the correct path for wol-3v3 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'wol-3v3'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/3/0/5'); + }); + + it('should return the correct path for wol-4v4 queue', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'wol-4v4'; + const teamType: StarcraftLeagueTeamType = 'arranged'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/4/0/5'); + }); + + it('should return the correct path for random team type', () => { + const seasonId = '42'; + const queueId: StarcraftLeagueQueue = 'wol-4v4'; + const teamType: StarcraftLeagueTeamType = 'random'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/42/4/1/5'); + }); + + it('should return the correct path for season 1', () => { + const seasonId = '1'; + const queueId: StarcraftLeagueQueue = 'wol-4v4'; + const teamType: StarcraftLeagueTeamType = 'random'; + const leagueId: StarcraftLeagueId = 'master'; + + const result = getLeagueData(seasonId, queueId, teamType, leagueId); + + expect(result.path).toBe('/data/sc2/league/1/4/1/5'); + }); +}); diff --git a/packages/sc2/src/league/league.ts b/packages/sc2/src/league/league.ts new file mode 100644 index 0000000..8e1d755 --- /dev/null +++ b/packages/sc2/src/league/league.ts @@ -0,0 +1,44 @@ +import type { Resource } from '@blizzard-api/core'; +import type { LeagueDataResponse, StarcraftLeagueId, StarcraftLeagueQueue, StarcraftLeagueTeamType } from './types'; + +const starcraftLeagueQueue: Record = { + 'hots-1v1': 101, + 'hots-2v2': 102, + 'hots-3v3': 103, + 'hots-4v4': 104, + 'lotv-1v1': 201, + 'lotv-2v2': 202, + 'lotv-3v3': 203, + 'lotv-4v4': 204, + 'lotv-archon': 206, + 'wol-1v1': 1, + 'wol-2v2': 2, + 'wol-3v3': 3, + 'wol-4v4': 4, +}; + +const starcraftLeagueTeam: Record = { + arranged: 0, + random: 1, +}; + +const starcraftLeagueId: Record = { + bronze: 0, + diamond: 4, + gold: 2, + grandmaster: 6, + master: 5, + platinum: 3, + silver: 1, +}; + +export function getLeagueData( + seasonId: string, + queueId: StarcraftLeagueQueue, + teamType: StarcraftLeagueTeamType, + leagueId: StarcraftLeagueId, +): Resource { + return { + path: `/data/sc2/league/${seasonId}/${starcraftLeagueQueue[queueId]}/${starcraftLeagueTeam[teamType]}/${starcraftLeagueId[leagueId]}`, + }; +} diff --git a/packages/sc2/src/league/types.ts b/packages/sc2/src/league/types.ts new file mode 100644 index 0000000..a95a40f --- /dev/null +++ b/packages/sc2/src/league/types.ts @@ -0,0 +1,43 @@ +import type { ResponseBase } from '../base'; + +export interface LeagueDataResponse extends ResponseBase { + key: Key; + tier: Array; +} + +interface Key { + league_id: number; + queue_id: number; + season_id: number; + team_type: number; +} + +interface Tier { + division: Array; + id: number; +} + +interface Division { + id: number; + ladder_id: number; + member_count: number; +} + +export type StarcraftLeagueQueue = + | 'hots-1v1' + | 'hots-2v2' + | 'hots-3v3' + | 'hots-4v4' + | 'lotv-1v1' + | 'lotv-2v2' + | 'lotv-3v3' + | 'lotv-4v4' + | 'lotv-archon' + | 'wol-1v1' + | 'wol-2v2' + | 'wol-3v3' + | 'wol-4v4'; + +export type StarcraftLeagueTeamType = 'arranged' | 'random'; + +export type StarcraftLeagueId = 'bronze' | 'diamond' | 'gold' | 'grandmaster' | 'master' | 'platinum' | 'silver'; diff --git a/packages/sc2/src/legacy/legacy.test.ts b/packages/sc2/src/legacy/legacy.test.ts new file mode 100644 index 0000000..5dbabb2 --- /dev/null +++ b/packages/sc2/src/legacy/legacy.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest'; +import { + legacyAchievements, + legacyLadder, + legacyLadders, + legacyMatchHistory, + legacyProfile, + legacyRewards, +} from './legacy'; + +describe('legacy', () => { + it('should generate correct path for legacyProfile', () => { + const result = legacyProfile('us', 1, 123_456); + expect(result.path).toBe('/sc2/legacy/profile/1/1/123456'); + }); + + it('should generate correct path for legacyLadders', () => { + const result = legacyLadders('eu', 2, 654_321); + expect(result.path).toBe('/sc2/legacy/profile/2/2/654321/ladders'); + }); + + it('should generate correct path for legacyMatchHistory', () => { + const result = legacyMatchHistory('kr', 1, 111_111); + expect(result.path).toBe('/sc2/legacy/profile/3/1/111111/matches'); + }); + + it('should generate correct path for legacyLadder', () => { + const result = legacyLadder('tw', 222_222); + expect(result.path).toBe('/sc2/legacy/ladder/3/222222'); + }); + + it('should generate correct path for legacyAchievements', () => { + const result = legacyAchievements('cn'); + expect(result.path).toBe('/sc2/legacy/data/achievements/5'); + }); + + it('should generate correct path for legacyRewards', () => { + const result = legacyRewards('us'); + expect(result.path).toBe('/sc2/legacy/data/rewards/1'); + }); +}); diff --git a/packages/sc2/src/legacy/legacy.ts b/packages/sc2/src/legacy/legacy.ts new file mode 100644 index 0000000..097371e --- /dev/null +++ b/packages/sc2/src/legacy/legacy.ts @@ -0,0 +1,57 @@ +import type { Resource } from '@blizzard-api/core'; +import { starcraftRegion, type StarcraftRegion } from '../base'; +import type { + LegacyAchievementsResponse, + LegacyLaddersResponse, + LegacyMatchHistoryResponse, + LegacyProfileResponse, + LegacyRewardsResponse, +} from './types'; + +export function legacyProfile( + regionId: StarcraftRegion, + realmId: 1 | 2, + profileId: number, +): Resource { + return { + path: `/sc2/legacy/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}`, + }; +} + +export function legacyLadders( + regionId: StarcraftRegion, + realmId: 1 | 2, + profileId: number, +): Resource { + return { + path: `/sc2/legacy/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}/ladders`, + }; +} + +export function legacyMatchHistory( + regionId: StarcraftRegion, + realmId: 1 | 2, + profileId: number, +): Resource { + return { + path: `/sc2/legacy/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}/matches`, + }; +} + +export function legacyLadder(regionId: StarcraftRegion, ladderId: number): Resource { + return { + path: `/sc2/legacy/ladder/${starcraftRegion[regionId]}/${ladderId}`, + }; +} + +export function legacyAchievements(regionId: StarcraftRegion): Resource { + return { + path: `/sc2/legacy/data/achievements/${starcraftRegion[regionId]}`, + }; +} + +export function legacyRewards(regionId: StarcraftRegion): Resource { + return { + path: `/sc2/legacy/data/rewards/${starcraftRegion[regionId]}`, + }; +} diff --git a/packages/sc2/src/legacy/types.ts b/packages/sc2/src/legacy/types.ts new file mode 100644 index 0000000..7275b43 --- /dev/null +++ b/packages/sc2/src/legacy/types.ts @@ -0,0 +1,142 @@ +export interface LegacyProfileResponse { + achievements: Achievements; + campaign: Campaign; + career: Career; + clanName: string; + clanTag: string; + displayName: string; + id: string; + portrait: Icon; + profilePath: string; + realm: number; + rewards: Rewards; + season: Season; + swarmLevels: SwarmLevels; +} + +interface Achievements { + achievements: Array<{ + achievementId: string; + completionDate: number; + }>; + points: Points; +} + +interface Points { + categoryPoints: Record; + totalPoints: number; +} + +interface Campaign { + hots: string; + wol: string; +} + +interface Career { + careerTotalGames: number; + highest1v1Rank: string; + highestTeamRank: string; + primaryRace: string; + protossWins: number; + seasonTotalGames: number; + terranWins: number; + zergWins: number; +} + +interface Icon { + h: number; + offset: number; + url: string; + w: number; + x: number; + y: number; +} + +interface Rewards { + earned: Array; + selected: Array; +} + +interface Season { + seasonId: number; + seasonNumber: number; + seasonYear: number; + stats: Array; + totalGamesThisSeason: number; +} + +interface Stat { + games: number; + type: string; + wins: number; +} + +interface SwarmLevels { + level: number; + protoss: SwarmLevelsByRace; + terran: SwarmLevelsByRace; + zerg: SwarmLevelsByRace; +} + +interface SwarmLevelsByRace { + currentLevelXP: number; + level: number; + totalLevelXP: number; +} + +export interface LegacyLaddersResponse { + currentSeason: Array; + previousSeason: Array; + showcasePlacement: Array; +} + +export interface LegacyMatchHistoryResponse { + matches: Array; +} + +interface Match { + date: number; + decision: 'Left' | 'Loss' | 'Win'; + map: string; + speed: 'Fast' | 'Faster'; + type: '2v2' | '3v3' | 'Co-Op' | 'Custom'; +} + +export interface LegacyAchievementsResponse { + achievements: Array; + categories: Array; +} + +interface Achievement { + achievementId: string; + categoryId: string; + description: string; + icon: Icon; + points: number; + title: string; +} + +interface Category { + categoryId: string; + children?: Array; + featuredAchievementId: string; + title: string; +} + +export interface LegacyRewardsResponse { + animations: Array; + portraits: Array; + protossDecals: Array; + skins: Array; + terranDecals: Array; + zergDecals: Array; +} + +interface Animation { + achievementId: string; + command?: '/dance'; + icon: Icon; + id: string; + name?: string; + title: string; +} diff --git a/packages/sc2/src/profile/profile.test.ts b/packages/sc2/src/profile/profile.test.ts new file mode 100644 index 0000000..85e54f1 --- /dev/null +++ b/packages/sc2/src/profile/profile.test.ts @@ -0,0 +1,37 @@ +import type { Resource } from '@blizzard-api/core'; +import { describe, expect, it } from 'vitest'; +import { starcraftRegion, type StarcraftRegion } from '../base'; +import { ladder, ladderSummary, metadata, profile, staticProfile } from './profile'; +import type { LadderResponse, LadderSummaryResponse, MetadataResponse, StaticProfileResponse } from './types'; + +describe('profile', () => { + const regionId: StarcraftRegion = 'us'; + const realmId = 1; + const profileId = 12_345; + const ladderId = 67_890; + + it('should generate correct path for staticProfile', () => { + const result: Resource = staticProfile(regionId); + expect(result.path).toBe(`/sc2/static/profile/${starcraftRegion[regionId]}`); + }); + + it('should generate correct path for metadata', () => { + const result: Resource = metadata(regionId, realmId, profileId); + expect(result.path).toBe(`/sc2/metadata/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}`); + }); + + it('should generate correct path for profile', () => { + const result: Resource = profile(regionId, realmId, profileId); + expect(result.path).toBe(`/sc2/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}`); + }); + + it('should generate correct path for ladderSummary', () => { + const result: Resource = ladderSummary(regionId, realmId, profileId); + expect(result.path).toBe(`/sc2/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}/ladder/summary`); + }); + + it('should generate correct path for ladder', () => { + const result: Resource = ladder(regionId, realmId, profileId, ladderId); + expect(result.path).toBe(`/sc2/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}/ladder/${ladderId}`); + }); +}); diff --git a/packages/sc2/src/profile/profile.ts b/packages/sc2/src/profile/profile.ts new file mode 100644 index 0000000..65ca2dc --- /dev/null +++ b/packages/sc2/src/profile/profile.ts @@ -0,0 +1,42 @@ +import type { Resource } from '@blizzard-api/core'; +import { starcraftRegion, type StarcraftRegion } from '../base'; +import type { LadderResponse, LadderSummaryResponse, MetadataResponse, StaticProfileResponse } from './types'; + +export function staticProfile(regionId: StarcraftRegion): Resource { + return { + path: `/sc2/static/profile/${starcraftRegion[regionId]}`, + }; +} + +export function metadata(regionId: StarcraftRegion, realmId: 1 | 2, profileId: number): Resource { + return { + path: `/sc2/metadata/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}`, + }; +} + +export function profile(regionId: StarcraftRegion, realmId: 1 | 2, profileId: number): Resource { + return { + path: `/sc2/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}`, + }; +} + +export function ladderSummary( + regionId: StarcraftRegion, + realmId: 1 | 2, + profileId: number, +): Resource { + return { + path: `/sc2/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}/ladder/summary`, + }; +} + +export function ladder( + regionId: StarcraftRegion, + realmId: 1 | 2, + profileId: number, + ladderId: number, +): Resource { + return { + path: `/sc2/profile/${starcraftRegion[regionId]}/${realmId}/${profileId}/ladder/${ladderId}`, + }; +} diff --git a/packages/sc2/src/profile/types.ts b/packages/sc2/src/profile/types.ts new file mode 100644 index 0000000..838ee4c --- /dev/null +++ b/packages/sc2/src/profile/types.ts @@ -0,0 +1,75 @@ +export interface StaticProfileResponse { + achievements: Array; + categories: Array; + criteria: Array; + rewards: Array; +} + +interface Achievement { + categoryId: string; + chainAchievementIds: Array; + chainRewardSize: number; + criteriaIds?: Array; + description: string; + flags: number; + id: string; + imageUrl: string; + isChained: boolean; + points: number; + title: string; + uiOrderHint: number; +} + +interface Category { + childrenCategoryIds: Array; + featuredAchievementId: string; + id: string; + medalTiers?: Array; + name: string; + parentCategoryId: null | string; + points: number; + uiOrderHint: number; +} + +interface Criterion { + achievementId: string; + description: string; + evaluationClass: 'Achv' | 'Clnt' | 'S2Gm' | 'Sunk' | 'Trny'; + flags: number; + id: string; + necessaryQuantity: number; + uiOrderHint: number; +} + +interface Reward { + achievementId?: string; + command?: '/dance'; + flags: number; + id: string; + imageUrl: string; + isSkin: boolean; + name: string; + uiOrderHint: number; + unlockableType: string; +} + +export interface MetadataResponse { + avatarUrl: string; + name: string; + profileId: string; + profileUrl: string; + realmId: number; + regionId: number; +} + +export interface LadderSummaryResponse { + allLadderMemberships: Array; + placementMatches: Array; + showCaseEntries: Array; +} + +export interface LadderResponse { + allLadderMemberships: Array; + ladderTeams: Array; + ranksAndPools: Array; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be250ac..96c0330 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,6 +78,9 @@ importers: '@blizzard-api/d3': specifier: workspace:* version: link:../d3 + '@blizzard-api/sc2': + specifier: workspace:* + version: link:../sc2 '@blizzard-api/wow': specifier: workspace:* version: link:../wow @@ -90,6 +93,12 @@ importers: specifier: workspace:* version: link:../core + packages/sc2: + devDependencies: + '@blizzard-api/core': + specifier: workspace:* + version: link:../core + packages/wow: devDependencies: '@blizzard-api/core':