├── .github ├── dependabot.yaml └── workflows │ ├── auto-merge.yml │ ├── build-docs.yaml │ ├── build.yaml │ └── publish.yaml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── .eslintrc.json ├── README.md ├── api-build.mjs ├── app │ ├── (home) │ │ ├── layout.tsx │ │ └── page.tsx │ ├── docs │ │ ├── [[...slug]] │ │ │ └── page.tsx │ │ └── layout.tsx │ ├── global.css │ ├── layout.config.tsx │ └── layout.tsx ├── content │ └── docs │ │ ├── api │ │ ├── lib.model.mdxmodel │ │ └── provider │ │ │ ├── achievements.model.mdxmodel │ │ │ ├── catalog.model.mdxmodel │ │ │ ├── gameclips.model.mdxmodel │ │ │ ├── gamepass.model.mdxmodel │ │ │ ├── messages.model.mdxmodel │ │ │ ├── people.model.mdxmodel │ │ │ ├── pins.model.mdxmodel │ │ │ ├── profile.model.mdxmodel │ │ │ ├── rest.model.mdxmodel │ │ │ ├── screenshots.model.mdxmodel │ │ │ ├── smartglass.model.mdxmodel │ │ │ ├── social.model.mdxmodel │ │ │ ├── titlehub.model.mdxmodel │ │ │ ├── userpresence.model.mdxmodel │ │ │ ├── usersearch.model.mdxmodel │ │ │ ├── userstats.model.mdxmodel │ │ │ └── xnotify.model.mdxmodel │ │ ├── changelogs │ │ ├── 2.0.0.mdx │ │ └── meta.json │ │ ├── cli.mdx │ │ ├── index.mdx │ │ ├── meta.json │ │ └── pagination.mdx ├── lib │ └── source.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.js ├── source.config.ts └── tsconfig.json ├── package-lock.json ├── package.json ├── src ├── bin │ └── cli.ts ├── lib.ts ├── lib │ └── http.ts ├── provider │ ├── achievements.ts │ ├── base.ts │ ├── catalog.ts │ ├── gameclips.ts │ ├── gamepass.ts │ ├── messages.ts │ ├── people.ts │ ├── pins.ts │ ├── profile.ts │ ├── rest.ts │ ├── screenshots.ts │ ├── smartglass.ts │ ├── social.ts │ ├── titlehub.ts │ ├── userpresence.ts │ ├── usersearch.ts │ ├── userstats.ts │ └── xnotify.ts └── types │ ├── achievements │ └── index.ts │ ├── catalog │ └── index.ts │ ├── gameclips │ └── index.ts │ ├── gamepass │ └── index.ts │ ├── messages │ └── index.ts │ ├── paging.ts │ ├── people │ └── index.ts │ ├── pins │ └── index.ts │ ├── profile │ └── index.ts │ ├── screenshots │ └── index.ts │ ├── smartglass │ └── index.ts │ ├── social │ └── index.ts │ ├── titlehub │ └── index.ts │ ├── userpresence │ └── index.ts │ ├── usersearch │ └── index.ts │ ├── userstats │ └── index.ts │ └── xnotify │ └── index.ts ├── tests └── lib.ts └── tsconfig.json /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | target-branch: "main" 10 | labels: 11 | - "github-actions dependencies" 12 | 13 | # Maintain dependencies for npm 14 | - package-ecosystem: "npm" 15 | directory: "/" 16 | schedule: 17 | interval: "weekly" 18 | target-branch: "main" 19 | labels: 20 | - "npm dependencies" 21 | 22 | - package-ecosystem: "npm" 23 | directory: "/" 24 | schedule: 25 | interval: "weekly" 26 | target-branch: "release/2.0.0" 27 | labels: 28 | - "npm dependencies" 29 | 30 | - package-ecosystem: "npm" 31 | directory: "docs/" 32 | schedule: 33 | interval: "weekly" 34 | target-branch: "release/2.0.0" 35 | labels: 36 | - "npm dependencies" -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot Automerge 2 | on: [pull_request_target] 3 | 4 | jobs: 5 | auto-merge: 6 | runs-on: ubuntu-latest 7 | if: github.actor == 'dependabot[bot]' 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 11 | with: 12 | target: minor 13 | github-token: ${{ secrets.SECRET_GITHUB }} -------------------------------------------------------------------------------- /.github/workflows/build-docs.yaml: -------------------------------------------------------------------------------- 1 | name: Build docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | - 'feature/*' 8 | - 'release/*' 9 | tags: 10 | - v* 11 | pull_request: 12 | branches: 13 | - main 14 | - release/2.0.0 15 | 16 | permissions: 17 | id-token: write 18 | pages: write 19 | 20 | # env: 21 | # DEBUG: '*' 22 | 23 | jobs: 24 | build: 25 | runs-on: ${{ matrix.os }} 26 | 27 | strategy: 28 | matrix: 29 | os: [ubuntu-latest] 30 | 31 | steps: 32 | - name: Checkout Git repository 33 | uses: actions/checkout@v4.2.2 34 | with: 35 | submodules: recursive 36 | 37 | - name: Setup Node.js 38 | uses: actions/setup-node@v4.2.0 39 | with: 40 | node-version: 22 41 | 42 | - name: Install npm dependencies 43 | working-directory: ./docs 44 | run: npm ci 45 | 46 | - name: Build project 47 | working-directory: ./docs 48 | run: npm run build 49 | 50 | - name: Upload static files as artifact 51 | id: deployment-pages 52 | uses: actions/upload-pages-artifact@v3 53 | with: 54 | path: docs/out/ 55 | 56 | # Deployment job 57 | deploy: 58 | environment: 59 | name: github-pages 60 | url: ${{ steps.deployment.outputs.page_url }} 61 | runs-on: ubuntu-latest 62 | needs: build 63 | if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release/2.0.0' || startsWith(github.ref, 'refs/tags/v') 64 | steps: 65 | - name: Deploy to GitHub Pages 66 | id: deployment-pages 67 | uses: actions/deploy-pages@v4 68 | 69 | - name: Info 70 | run: echo "Deployed to ${{ steps.deployment-pages.outputs.page_url }}" -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build/release 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | - 'feature/*' 8 | - 'release/*' 9 | tags: 10 | - v* 11 | pull_request: 12 | branches: 13 | - main 14 | 15 | # env: 16 | # DEBUG: '*' 17 | 18 | jobs: 19 | release: 20 | runs-on: ${{ matrix.os }} 21 | 22 | strategy: 23 | matrix: 24 | os: [macos-latest, ubuntu-latest, windows-latest] 25 | 26 | steps: 27 | - name: Checkout Git repository 28 | uses: actions/checkout@v4.2.2 29 | with: 30 | fetch-depth: 0 31 | submodules: recursive 32 | 33 | - name: Setup Node.js 34 | uses: actions/setup-node@v4.2.0 35 | with: 36 | node-version: 22 37 | 38 | - name: SonarQube Scan 39 | uses: SonarSource/sonarqube-scan-action@v4 40 | env: 41 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 42 | with: 43 | args: | 44 | -Dsonar.organization=unknownskl-github \ 45 | -Dsonar.projectKey=unknownskl_xbox-webapi-node \ 46 | -Dsonar.sources=./src 47 | if: matrix.os == 'ubuntu-latest' && github.event_name != 'pull_request' 48 | 49 | - name: Install npm dependencies 50 | run: npm ci 51 | 52 | - name: Build project 53 | run: npm run build 54 | 55 | - name: Run NodeJS Tests 56 | run: npm run test 57 | 58 | # - name: Set release version 59 | # run: python3 -c "import os; tag = os.environ['GITHUB_REF'].split('/')[-1]; f = open(os.environ['GITHUB_ENV'], 'a'); f.write('RELEASE_VERSION='+tag); f.close();" -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: NPM Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | # the Node.js versions to build on 15 | node-version: [22.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v4.2.2 19 | with: 20 | persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token 21 | fetch-depth: 0 # otherwise, you will failed to push refs to dest repo 22 | 23 | - name: Get tag name 24 | id: tag_name 25 | run: | 26 | echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/} 27 | 28 | - name: Install dependencies 29 | run: npm ci 30 | 31 | - name: Build the project 32 | run: npm run build 33 | 34 | - name: Run NodeJS Tests 35 | run: npm run test 36 | 37 | - name: NPM publish beta package 38 | if: ${{ contains(steps.tag_name.outputs.SOURCE_TAG, 'beta') }} 39 | uses: JS-DevTools/npm-publish@v3 40 | with: 41 | token: ${{ secrets.NPM_TOKEN }} 42 | tag: beta 43 | 44 | - name: NPM publish package 45 | if: ${{ !contains(steps.tag_name.outputs.SOURCE_TAG, 'beta') }} 46 | uses: JS-DevTools/npm-publish@v3 47 | with: 48 | token: ${{ secrets.NPM_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | index.node 3 | **/node_modules 4 | **/.DS_Store 5 | npm-debug.log* 6 | dist/ 7 | .xbox.tokens.json 8 | .xbox.tokens*.json 9 | 10 | # generated content 11 | .contentlayer 12 | .content-collections 13 | .source 14 | out/ 15 | docs/content/docs/api/*.mdx 16 | docs/content/docs/api/*/*.mdx 17 | !docs/content/docs/api/*.model.mdx 18 | !docs/content/docs/api/*/*.model.mdx 19 | .nyc_output/ 20 | 21 | # test & build 22 | docs/coverage 23 | coverage/ 24 | docs/.next/ 25 | docs/out/ 26 | docs/build 27 | *.tsbuildinfo 28 | 29 | # misc 30 | .DS_Store 31 | *.pem 32 | /.pnp 33 | .pnp.js 34 | npm-debug.log* 35 | yarn-debug.log* 36 | yarn-error.log* 37 | 38 | # others 39 | .env*.local 40 | .vercel 41 | next-env.d.ts 42 | notes.md 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 UnknownSKL (Jim Kroon) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xbox-Webapi-Node 2 | 3 | **xbox-webapi-node:** Typescript implementation for Xbox WebAPI's 4 | 5 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=unknownskl_xbox-webapi-node&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=unknownskl_xbox-webapi-node) 6 | [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=unknownskl_xbox-webapi-node&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=unknownskl_xbox-webapi-node) 7 | [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=unknownskl_xbox-webapi-node&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=unknownskl_xbox-webapi-node) 8 | [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=unknownskl_xbox-webapi-node&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=unknownskl_xbox-webapi-node) 9 | 10 | 11 | 📚 Documentation: [https://unknownskl.github.io/xbox-webapi-node/](https://unknownskl.github.io/xbox-webapi-node/) -------------------------------------------------------------------------------- /docs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | // "extends": ["next/core-web-vitals", "next/typescript"] 3 | "extends": ["next/typescript"] 4 | } 5 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # docs 2 | 3 | This is a Next.js application generated with 4 | [Create Fumadocs](https://github.com/fuma-nama/fumadocs). 5 | 6 | Run development server: 7 | 8 | ```bash 9 | npm run dev 10 | # or 11 | pnpm dev 12 | # or 13 | yarn dev 14 | ``` 15 | 16 | Open http://localhost:3000 with your browser to see the result. 17 | 18 | ## Learn More 19 | 20 | To learn more about Next.js and Fumadocs, take a look at the following 21 | resources: 22 | 23 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js 24 | features and API. 25 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 26 | - [Fumadocs](https://fumadocs.vercel.app) - learn about Fumadocs 27 | -------------------------------------------------------------------------------- /docs/api-build.mjs: -------------------------------------------------------------------------------- 1 | import { generateFiles, createGenerator } from 'fumadocs-typescript'; 2 | import * as path from 'node:path'; 3 | 4 | const generator = createGenerator() 5 | 6 | void generateFiles(generator, { 7 | input: ['./content/docs/**/*.model.mdxmodel'], 8 | // Rename x.model.mdx to x.mdx 9 | output: (file) => 10 | path.resolve( 11 | path.dirname(file), 12 | `${path.basename(file).split('.')[0]}.mdx`, 13 | ), 14 | }); -------------------------------------------------------------------------------- /docs/app/(home)/layout.tsx: -------------------------------------------------------------------------------- 1 | // import type { ReactNode } from 'react'; 2 | // import { HomeLayout } from 'fumadocs-ui/layouts/home'; 3 | // import { baseOptions } from '@/app/layout.config'; 4 | 5 | // export default function Layout({ children }: { children: ReactNode }) { 6 | // return {children}; 7 | // } 8 | 9 | 10 | import { DocsLayout } from 'fumadocs-ui/layouts/docs'; 11 | import type { ReactNode } from 'react'; 12 | import { baseOptions } from '@/app/layout.config'; 13 | import { source } from '@/lib/source'; 14 | 15 | export default function Layout({ children }: { children: ReactNode }) { 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /docs/app/(home)/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | 3 | export default function HomePage() { 4 | return ( 5 |
6 |

Xbox-Webapi-Node Documentation

7 |

8 | Check out{' '} 9 | 13 | getting started 14 | {' '} 15 | to start using xbox-webapi-node. 16 |

17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /docs/app/docs/[[...slug]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { source } from '@/lib/source'; 2 | import { 3 | DocsPage, 4 | DocsBody, 5 | DocsDescription, 6 | DocsTitle, 7 | } from 'fumadocs-ui/page'; 8 | import { notFound } from 'next/navigation'; 9 | import defaultMdxComponents from 'fumadocs-ui/mdx'; 10 | 11 | export default async function Page(props: { 12 | params: Promise<{ slug?: string[] }>; 13 | }) { 14 | const params = await props.params; 15 | const page = source.getPage(params.slug); 16 | if (!page) notFound(); 17 | 18 | const MDX = page.data.body; 19 | 20 | return ( 21 | 22 | {page.data.title} 23 | {page.data.description} 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | export async function generateStaticParams() { 32 | return source.generateParams(); 33 | } 34 | 35 | export async function generateMetadata(props: { 36 | params: Promise<{ slug?: string[] }>; 37 | }) { 38 | const params = await props.params; 39 | const page = source.getPage(params.slug); 40 | if (!page) notFound(); 41 | 42 | return { 43 | title: page.data.title, 44 | description: page.data.description, 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /docs/app/docs/layout.tsx: -------------------------------------------------------------------------------- 1 | import { DocsLayout } from 'fumadocs-ui/layouts/docs'; 2 | import type { ReactNode } from 'react'; 3 | import { baseOptions } from '@/app/layout.config'; 4 | import { source } from '@/lib/source'; 5 | 6 | export default function Layout({ children }: { children: ReactNode }) { 7 | return ( 8 | 9 | {children} 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /docs/app/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @import 'tailwindcss'; 6 | @import 'fumadocs-ui/css/neutral.css'; 7 | @import 'fumadocs-ui/css/preset.css'; 8 | 9 | /* relative to the CSS file, make sure it's correct for your app */ 10 | @source '../node_modules/fumadocs-ui/dist/**/*.js'; -------------------------------------------------------------------------------- /docs/app/layout.config.tsx: -------------------------------------------------------------------------------- 1 | import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared'; 2 | 3 | /** 4 | * Shared layout configurations 5 | * 6 | * you can configure layouts individually from: 7 | * Home Layout: app/(home)/layout.tsx 8 | * Docs Layout: app/docs/layout.tsx 9 | */ 10 | export const baseOptions: BaseLayoutProps = { 11 | nav: { 12 | title: 'Xbox-Webapi-Node', 13 | url: '/docs' 14 | }, 15 | githubUrl: 'https://github.com/unknownskl/xbox-webapi-node', 16 | links: [ 17 | { 18 | text: 'Home', 19 | url: '/', 20 | active: 'nested-url', 21 | }, 22 | { 23 | text: 'Documentation', 24 | url: '/docs', 25 | active: 'nested-url', 26 | }, 27 | ], 28 | }; 29 | -------------------------------------------------------------------------------- /docs/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import './global.css'; 2 | import { RootProvider } from 'fumadocs-ui/provider'; 3 | import { Inter } from 'next/font/google'; 4 | import type { ReactNode } from 'react'; 5 | 6 | const inter = Inter({ 7 | subsets: ['latin'], 8 | }); 9 | 10 | export default function Layout({ children }: { children: ReactNode }) { 11 | return ( 12 | 13 | 14 | {children} 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /docs/content/docs/api/lib.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: XboxWebApi 3 | description: XboxWebApi Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../src/lib.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/achievements.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: AchievementsProvider 3 | description: AchievementsProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/achievements.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/catalog.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: CatalogProvider 3 | description: CatalogProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/catalog.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/gameclips.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: GameclipsProvider 3 | description: GameclipsProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/gameclips.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/gamepass.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: GamepassProvider 3 | description: GamepassProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/gamepass.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/messages.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: MessagesProvider 3 | description: MessagesProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/messages.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/people.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: PeopleProvider 3 | description: PeopleProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/people.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/pins.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: PinsProvider 3 | description: PinsProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/pins.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/profile.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: ProfileProvider 3 | description: ProfileProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/profile.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/rest.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: RestProvider 3 | description: RestProvider Class 4 | --- 5 | 6 | ## Example code 7 | 8 | ```ts 9 | // Retrieve achievements of a user 10 | const result = await api.providers.rest.getRequest('https://achievements.xboxlive.com/users/xuid()/history/titles') 11 | 12 | // The response has a continuationToken. We can call .next() to retrieve the next page 13 | const result2 = await result.next() 14 | 15 | // Print out page 2 of the achievements api call 16 | console.log(result2) 17 | ``` 18 | 19 | ---type-table--- 20 | ../../../../../src/provider/rest.ts 21 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/screenshots.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: ScreenshotsProvider 3 | description: ScreenshotsProvider Class 4 | --- 5 | 6 | 7 | This api is outdated. The api still works but no results are returned. 8 | 9 | 10 | ---type-table--- 11 | ../../../../../src/provider/screenshots.ts 12 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/smartglass.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: SmartglassProvider 3 | description: SmartglassProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/smartglass.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/social.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: SocialProvider 3 | description: SocialProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/social.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/titlehub.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: TitlehubProvider 3 | description: TitlehubProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/titlehub.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/userpresence.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: UserpresenceProvider 3 | description: UserpresenceProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/userpresence.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/usersearch.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: UsersearchProvider 3 | description: UsersearchProvider Class 4 | --- 5 | 6 | 7 | This api is outdated. The api still works but returns null results. 8 | 9 | 10 | ---type-table--- 11 | ../../../../../src/provider/usersearch.ts 12 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/userstats.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: UserstatsProvider 3 | description: UserstatsProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/userstats.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/api/provider/xnotify.model.mdxmodel: -------------------------------------------------------------------------------- 1 | --- 2 | title: XnotifyProvider 3 | description: XnotifyProvider Class 4 | --- 5 | 6 | ---type-table--- 7 | ../../../../../src/provider/xnotify.ts 8 | ---end--- -------------------------------------------------------------------------------- /docs/content/docs/changelogs/2.0.0.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: v2.0.0 3 | description: Changelogs for v2.0.0 4 | --- 5 | 6 | Changelog: 7 | 8 | - Rewrite of `xbox-webapi` package -------------------------------------------------------------------------------- /docs/content/docs/changelogs/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "2.0.0" 4 | ] 5 | } -------------------------------------------------------------------------------- /docs/content/docs/cli.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: CLI 3 | description: Xbox-Webapi CLI 4 | --- 5 | 6 | Xbox-Webapi-Node also offers a CLI application so you can make api calls to the xbox api from your terminal. 7 | 8 | ### Install 9 | 10 | To install the CLI, use the following command: 11 | 12 | ```npm install -g xbox-webapi``` 13 | 14 | Once the package is installed the application should be available using `xbox-webapi`. 15 | 16 | ### Available commands 17 | 18 | | Command | Description | 19 | |---------|-------------| 20 | | `xbox-webapi` | Shows help and commands | 21 | 22 | Example output: 23 | 24 | ```shell 25 | xbox-webapi [args] 26 | 27 | Commands: 28 | xbox-webapi achievements [command] achievements provider 29 | xbox-webapi messages [command] messages provider 30 | xbox-webapi userpresence [command] userpresence provider 31 | xbox-webapi smartglass [command] smartglass provider 32 | xbox-webapi catalog [command] catalog provider 33 | xbox-webapi gameclips [command] gameclips provider 34 | xbox-webapi screenshots [command] screenshots provider 35 | xbox-webapi people [command] people provider 36 | xbox-webapi pins [command] pins provider 37 | xbox-webapi profile [command] profile provider 38 | xbox-webapi social [command] social provider 39 | xbox-webapi titlehub [command] titlehub provider 40 | xbox-webapi usersearch [command] usersearch provider 41 | xbox-webapi userstats [command] userstats provider 42 | xbox-webapi xnotify [command] xnotify provider 43 | 44 | Options: 45 | --help Show help [boolean] 46 | --version Show version number [boolean] 47 | -v, --verbose Run with verbose logging [boolean] 48 | -o, --output Output 49 | [string] [choices: "table", "json", "jsonp"] [default: "table"] 50 | -c, --continuationToken Continuation token [number] 51 | -n, --maxItems Max items [number] 52 | -s, --skipItems Skip items [number] 53 | -m, --market Market [string] [default: "US"] 54 | -l, --language Language [string] [default: "en-US"] 55 | 56 | You need to specify a command. Use --help to see available commands. 57 | ``` 58 | 59 | ### Render output 60 | 61 | The default output of the cli is using `console.table`. This will try to render the data, however in most cases this is useless. 62 | 63 | You can supply the `-o json` argument to change the output to json. 64 | 65 | You can also use `-o jsonp` to output pretty printed json. -------------------------------------------------------------------------------- /docs/content/docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Starting with Xbox-Webapi-Node 4 | --- 5 | 6 | ### Installing xbox-webapi 7 | 8 | You can install the project with any package manager you like. In the project directory, run: 9 | 10 | For NPM: 11 | ```sh 12 | $ npm install xbox-webapi 13 | ``` 14 | 15 | For Yarn: 16 | ```sh 17 | $ yarn add xbox-webapi 18 | ``` 19 | 20 | If you want to install xbox-webapi-node for cli purposes, run the following: 21 | 22 | For NPM: 23 | ```sh 24 | $ npm install -g xbox-webapi 25 | ``` -------------------------------------------------------------------------------- /docs/content/docs/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "pages": [ 4 | "--- Documentation ---", 5 | "index", 6 | "cli", 7 | "...", 8 | 9 | "--- API Definitions ---", 10 | "...api", 11 | 12 | "--- Changelogs ---", 13 | "...changelogs" 14 | ] 15 | } -------------------------------------------------------------------------------- /docs/content/docs/pagination.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pagination 3 | description: Pagination system 4 | --- 5 | 6 | The library offers an easy way to paginate through api calls. For example the achievements provider uses the continuationToken to navigate through multiple lists. 7 | 8 | To help the user to navigate this we can simply make a api call using the example code below: 9 | 10 | ```ts 11 | const achievements = await api.providers.achievements.getAchievements(xuid) // Retrieve achievements of the user 12 | console.log(achievements.data.titles) // Print out the first list of achievements 13 | 14 | const achievements2 = await achievements.next() // Load second list of achievements. 15 | console.log(achievements2.data.titles) // Print out the second list of achievements 16 | 17 | const achievements3 = await achievements2.next() // Load third list of achievements. 18 | console.log(achievements2.data.titles) // Print out the third list of achievements 19 | ``` 20 | 21 | All the methods which response contains a continuationToken will support this way of navigating. (Including the `rest` provider) -------------------------------------------------------------------------------- /docs/lib/source.ts: -------------------------------------------------------------------------------- 1 | import { docs, meta } from '@/.source'; 2 | import { createMDXSource } from 'fumadocs-mdx'; 3 | import { loader } from 'fumadocs-core/source'; 4 | 5 | export const source = loader({ 6 | baseUrl: '/docs', 7 | source: createMDXSource(docs, meta), 8 | }); 9 | -------------------------------------------------------------------------------- /docs/next.config.mjs: -------------------------------------------------------------------------------- 1 | import { createMDX } from 'fumadocs-mdx/next'; 2 | 3 | const withMDX = createMDX(); 4 | 5 | /** @type {import('next').NextConfig} */ 6 | const config = { 7 | output: 'export', 8 | reactStrictMode: true, 9 | basePath: '/xbox-webapi-node', 10 | }; 11 | 12 | export default withMDX(config); 13 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "node api-build.mjs && next build", 7 | "dev": "node api-build.mjs && next dev", 8 | "start": "next start", 9 | "postinstall": "fumadocs-mdx" 10 | }, 11 | "dependencies": { 12 | "fumadocs-core": "^15.5.0", 13 | "fumadocs-mdx": "^11.6.7", 14 | "fumadocs-typescript": "^4.0.5", 15 | "fumadocs-ui": "^15.5.1", 16 | "next": "^15.3.3", 17 | "react": "^19.0.0", 18 | "react-dom": "^19.1.0" 19 | }, 20 | "devDependencies": { 21 | "@tailwindcss/postcss": "^4.1.7", 22 | "@types/mdx": "^2.0.13", 23 | "@types/node": "^22.15.30", 24 | "@types/react": "^19.1.7", 25 | "@types/react-dom": "^19.1.5", 26 | "autoprefixer": "^10.4.20", 27 | "eslint": "^9", 28 | "eslint-config-next": "^15.3.3", 29 | "postcss": "^8.5.4", 30 | "tailwindcss": "^4.1.8", 31 | "typescript": "^5.8.3" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | "@tailwindcss/postcss": {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /docs/source.config.ts: -------------------------------------------------------------------------------- 1 | import { defineDocs, defineConfig } from 'fumadocs-mdx/config'; 2 | 3 | export const { docs, meta } = defineDocs({ 4 | dir: 'content/docs', 5 | }); 6 | 7 | export default defineConfig(); 8 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "bundler", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "paths": { 19 | "@/*": ["./*"] 20 | }, 21 | "plugins": [ 22 | { 23 | "name": "next" 24 | } 25 | ] 26 | }, 27 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 28 | "exclude": ["node_modules"] 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xbox-webapi", 3 | "version": "2.0.0-beta3", 4 | "description": "Xbox Web API client in Node.JS that supports Smartglass commands to control the Xbox", 5 | "main": "dist/lib.js", 6 | "bin": { 7 | "xbox-webapi": "dist/bin/cli.js" 8 | }, 9 | "scripts": { 10 | "build": "rm -rf dist/ && tsc && chmod +x dist/bin/cli.js", 11 | "test": "npm run build && nyc --require ts-node/register --reporter=html --reporter=lcov --reporter=text mocha tests/**.ts", 12 | "start": "npm run build && node dist/bin/cli.js", 13 | "start:test": "npm run build && node dist/bin/test.js", 14 | "auth": "xbox-auth auth --auth msal" 15 | }, 16 | "author": "UnknownSKL ", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "@types/chai": "^5", 20 | "@types/mocha": "^10", 21 | "@types/node": "^22.5.4", 22 | "chai": "^5.1.1", 23 | "mocha": "^11.1.0", 24 | "nyc": "^17.1.0", 25 | "ts-node": "^10.9.2", 26 | "typescript": "^5.3.3", 27 | "uuid4": "^2.0.3", 28 | "xal-node": "^1.1.0", 29 | "yargs": "^17.7.2" 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/unknownskl/xbox-webapi-node.git" 34 | }, 35 | "keywords": [ 36 | "xbox", 37 | "webapi", 38 | "api" 39 | ], 40 | "bugs": { 41 | "url": "https://github.com/unknownskl/xbox-webapi-node/issues" 42 | }, 43 | "homepage": "https://github.com/unknownskl/xbox-webapi-node#readme" 44 | } 45 | -------------------------------------------------------------------------------- /src/bin/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import yargs from 'yargs' 3 | import ownPackage from '../../package.json' 4 | import XboxWebApi from '../lib' 5 | 6 | import { Xal, Msal, TokenStore } from 'xal-node' 7 | 8 | class Cli { 9 | _yargs:yargs 10 | _tokenStore:TokenStore 11 | _xal:Xal|Msal 12 | 13 | constructor() { 14 | 15 | this._yargs = yargs 16 | .scriptName("xbox-webapi") 17 | .usage('$0 [args]') 18 | .version(ownPackage.version) 19 | .detectLocale(false) 20 | .wrap(yargs.terminalWidth()) 21 | .demandCommand(1, 'You need to specify a command. Use --help to see available commands.'); 22 | 23 | this._tokenStore = new TokenStore() 24 | this._tokenStore.load('./.xbox.tokens.json') 25 | this._tokenStore.getAuthenticationMethod() 26 | if(this._tokenStore.getAuthenticationMethod() === 'msal'){ 27 | this._xal = new Msal(this._tokenStore) 28 | } else { 29 | this._xal = new Xal(this._tokenStore) 30 | } 31 | 32 | // Load providers and commands 33 | this._yargs = this._populateCommands(this._yargs) 34 | 35 | this._yargs.option('verbose', { 36 | alias: 'v', 37 | type: 'boolean', 38 | description: 'Run with verbose logging' 39 | }) 40 | 41 | this._yargs.option('output', { 42 | alias: 'o', 43 | type: 'string', 44 | description: 'Output', 45 | default: 'table' 46 | }).choices('output', ['table', 'json', 'jsonp']) 47 | 48 | yargs.option('continuationToken', { 49 | alias: 'c', 50 | type: 'number', 51 | description: 'Continuation token', 52 | }).option('maxItems', { 53 | alias: 'n', 54 | type: 'number', 55 | description: 'Max items', 56 | }).option('skipItems', { 57 | alias: 's', 58 | type: 'number', 59 | description: 'Skip items', 60 | }).option('market', { 61 | alias: 'm', 62 | type: 'string', 63 | description: 'Market', 64 | default: 'US' 65 | }).option('language', { 66 | alias: 'l', 67 | type: 'string', 68 | description: 'Language', 69 | default: 'en-US' 70 | }) 71 | } 72 | 73 | run() { 74 | this._yargs.argv 75 | } 76 | 77 | _getFunctionArgs(func) { 78 | const args = func.toString().match(/\(([^)]*)\)/); 79 | const args2 = args ? args[1].split(',').map(arg => arg.trim().split(' ')[0]).filter(arg => arg) : []; 80 | return args2 81 | } 82 | 83 | _populateCommands(yargs: yargs) { 84 | // Load providers and commands 85 | 86 | // Create dummy api and check ffor providers and commands in class 87 | const dummyApi = new XboxWebApi({ 88 | uhs: 'dummy', 89 | token: 'dummy' 90 | }); 91 | 92 | for(const provider in dummyApi.providers){ 93 | const commands = this._getCommandsFromProvider(dummyApi.providers[provider]) 94 | 95 | yargs.command( 96 | `${provider} [command]`, 97 | `${provider} provider`, 98 | (yargs) => { 99 | 100 | // Setup commands in providers 101 | for(const command in commands){ 102 | const functionArgs = this._getFunctionArgs(dummyApi.providers[provider][commands[command]]) 103 | 104 | const functionArgsFiltered = functionArgs.filter((arg) => { 105 | return arg !== 'continuationToken' && 106 | arg !== 'maxItems' && 107 | arg !== 'skipItems' 108 | }) 109 | 110 | const commandStr = commands[command] + '' + (functionArgsFiltered.length > 0 ? ' [' + functionArgsFiltered.join('] [') + ']' : '') 111 | const descriptionStr = '' 112 | 113 | yargs.command( 114 | commandStr, 115 | descriptionStr, 116 | (yargs) => { 117 | for(const arg in functionArgs){ 118 | yargs.positional(functionArgs[arg], { 119 | type: 'string', 120 | }); 121 | } 122 | }, 123 | (argv) => { 124 | this._xal.getWebToken().then((token) => { 125 | 126 | const api = new XboxWebApi({ 127 | uhs: token.data.DisplayClaims.xui[0].uhs, 128 | token: token.data.Token, 129 | }) 130 | 131 | const args = []; 132 | for(const arg in functionArgs){ 133 | args.push(argv[functionArgs[arg]]) 134 | } 135 | 136 | if(argv.verbose === true) 137 | console.log(`Running command: ${provider}::${commands[command]}(${args})`); 138 | 139 | api.providers[provider][commands[command]](...args).then((result) => { 140 | 141 | // Render output 142 | if(argv.output === 'json'){ 143 | console.log(JSON.stringify(result.data)) 144 | 145 | } else if(argv.output === 'jsonp'){ 146 | console.log(JSON.stringify(result.data, null, 2)) 147 | 148 | } else { 149 | console.table(result.data) 150 | } 151 | 152 | }).catch((error) => { 153 | console.log('Failed to execute command:', error) 154 | }) 155 | 156 | }).catch((error) => { 157 | console.log('Failed to retrieve web token:', error) 158 | }) 159 | } 160 | ); 161 | } 162 | 163 | }, 164 | (argv) => { 165 | yargs.showHelp() 166 | } 167 | ); 168 | } 169 | 170 | return yargs 171 | } 172 | 173 | _getCommandsFromProvider(provider: any) { 174 | const cmds:string[] = [] 175 | const commands = Object.getOwnPropertyNames(Object.getPrototypeOf(provider)) 176 | for(const command in commands){ 177 | if(commands[command] === 'constructor') 178 | continue; 179 | 180 | if(commands[command].startsWith('_')) 181 | continue; 182 | 183 | // console.log('command:', commands[command]) 184 | cmds.push(commands[command]) 185 | } 186 | 187 | return cmds 188 | } 189 | } 190 | 191 | const cli = new Cli(); 192 | cli.run() -------------------------------------------------------------------------------- /src/lib.ts: -------------------------------------------------------------------------------- 1 | import SmartglassProvider from './provider/smartglass' 2 | import MessagesProvider from './provider/messages' 3 | import AchievementsProvider from './provider/achievements' 4 | import UserpresenceProvider from './provider/userpresence' 5 | import CatalogProvider from './provider/catalog' 6 | import GameclipsProvider from './provider/gameclips' 7 | import ScreenshotsProvider from './provider/screenshots' 8 | import PeopleProvider from './provider/people' 9 | import PinsProvider from './provider/pins' 10 | import ProfileProvider from './provider/profile' 11 | import SocialProvider from './provider/social' 12 | import TitlehubProvider from './provider/titlehub' 13 | import UsersearchProvider from './provider/usersearch' 14 | import UserstatsProvider from './provider/userstats' 15 | import XnotifyProvider from './provider/xnotify' 16 | import GamepassProvider from './provider/gamepass' 17 | import RestProvider from './provider/rest' 18 | 19 | type XboxWebApiConfig = { 20 | uhs: string 21 | token: string 22 | market?: string 23 | } 24 | 25 | export default class XboxWebApi { 26 | 27 | private readonly _config:XboxWebApiConfig 28 | 29 | constructor(config:XboxWebApiConfig){ 30 | this._config = { 31 | market: 'en-us', 32 | ...config 33 | } 34 | } 35 | 36 | providers = { 37 | 'achievements': new AchievementsProvider(this), 38 | 'messages': new MessagesProvider(this), 39 | 'userpresence': new UserpresenceProvider(this), 40 | 'smartglass': new SmartglassProvider(this), 41 | 'catalog': new CatalogProvider(this), 42 | 'gameclips': new GameclipsProvider(this), 43 | 'gamepass': new GamepassProvider(this), 44 | 'screenshots': new ScreenshotsProvider(this), 45 | 'people': new PeopleProvider(this), 46 | 'pins': new PinsProvider(this), 47 | 'profile': new ProfileProvider(this), 48 | 'social': new SocialProvider(this), 49 | 'titlehub': new TitlehubProvider(this), 50 | 'usersearch': new UsersearchProvider(this), 51 | 'userstats': new UserstatsProvider(this), 52 | 'xnotify': new XnotifyProvider(this), 53 | 'rest': new RestProvider(this), 54 | } 55 | 56 | getAuthorizationHeader(){ 57 | return 'XBL3.0 x='+this._config.uhs+';'+this._config.token 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/lib/http.ts: -------------------------------------------------------------------------------- 1 | import { IncomingHttpHeaders } from 'http' 2 | import https from 'https' 3 | import { URL } from 'url' 4 | 5 | export default class Http { 6 | 7 | getRequest(host:string, path:string, headers:any, method = 'GET') { 8 | return new Promise((resolve, reject) => { 9 | 10 | const hostHeaders = { 11 | ...headers, 12 | } 13 | 14 | const options = { 15 | method: method, 16 | hostname: host, 17 | path: path, 18 | port: 443, 19 | headers: hostHeaders 20 | } 21 | 22 | const req = this.createRequest(options, resolve, reject) 23 | 24 | req.on('error', (error) => { 25 | reject(new Error('Unhandled error:'+ JSON.stringify(error))) 26 | }) 27 | 28 | req.end() 29 | 30 | }) 31 | } 32 | 33 | deleteRequest(host:string, path:string, headers:any) { 34 | return this.getRequest(host, path, headers, 'DELETE') 35 | } 36 | 37 | postRequest(host:string, path:string, headers:any, data:any, method = 'POST') { 38 | return new Promise((resolve, reject) => { 39 | 40 | const hostHeaders = { 41 | ...headers, 42 | } 43 | 44 | if(typeof data === 'object'){ 45 | data = JSON.stringify(data) 46 | } 47 | 48 | const options = { 49 | method: method, 50 | hostname: host, 51 | path: path, 52 | port: 443, 53 | headers: hostHeaders 54 | } 55 | 56 | const req = this.createRequest(options, resolve, reject) 57 | 58 | req.on('error', (error) => { 59 | reject(new Error('Unhandled error:'+ JSON.stringify(error))) 60 | }) 61 | 62 | req.write(data) 63 | req.end() 64 | 65 | }) 66 | } 67 | 68 | putRequest(host:string, path:string, headers:any, data:any) { 69 | return this.postRequest(host, path, headers, data, 'PUT') 70 | } 71 | 72 | createRequest(options:any, resolve:any, reject:any){ 73 | return https.request(options, (res) => { 74 | let responseData = '' 75 | 76 | res.on('data', (data) => { 77 | responseData += data 78 | }) 79 | 80 | res.on('close', () => { 81 | if(res.statusCode == 200 || res.statusCode == 204){ 82 | if(responseData.toString() === ''){ 83 | resolve(new HttpResponse({}, res.headers, options)) 84 | } else { 85 | resolve(new HttpResponse(JSON.parse(responseData.toString()), res.headers, options)) 86 | } 87 | } else { 88 | reject(new Error('Error fetching '+options.hostname+options.path+'. Details: '+ JSON.stringify({ 89 | statuscode: res.statusCode, 90 | headers: res.headers, 91 | body: responseData.toString(), 92 | message: 'Error fetching '+options.hostname+options.path 93 | }, null, 2))) 94 | } 95 | }) 96 | }) 97 | } 98 | } 99 | 100 | 101 | export class HttpResponse { 102 | 103 | data:T 104 | headers:IncomingHttpHeaders 105 | options:any 106 | 107 | constructor(data:any, headers:IncomingHttpHeaders, options:any){ 108 | this.data = data 109 | this.headers = headers 110 | this.options = options 111 | 112 | // delete(this.options.headers['Authorization']) 113 | } 114 | 115 | header(){ 116 | return this.headers 117 | } 118 | 119 | body(){ 120 | return this.data 121 | } 122 | 123 | async next(){ 124 | const parsedUrl = new URL(this.options.path, 'https://dummy-base') 125 | const maxItems = parsedUrl.searchParams.get('maxItems') ?? undefined 126 | const skipItems = parsedUrl.searchParams.get('skipItems') ?? undefined 127 | 128 | // @ts-expect-error We already acknowledge the null value 129 | const continuationToken = this.data.pagingInfo?.continuationToken 130 | if(continuationToken === undefined){ 131 | throw new Error('No continuation token found in response') 132 | } 133 | 134 | const request = new Http() 135 | const path = parsedUrl.pathname + parsedUrl.search 136 | return (await request.getRequest(this.options.hostname, applyPagination(path, maxItems, skipItems, continuationToken), this.options.headers)) as HttpResponse 137 | } 138 | } 139 | 140 | export function applyPagination(path:string, maxItems, skipItems, continuationToken:undefined|string){ 141 | const parsedUrl = new URL(path, 'https://dummy-base') 142 | 143 | if(maxItems !== undefined) 144 | parsedUrl.searchParams.set('maxItems', maxItems) 145 | 146 | if(skipItems !== undefined) 147 | parsedUrl.searchParams.set('skipItems', skipItems) 148 | 149 | if(continuationToken !== undefined) 150 | parsedUrl.searchParams.set('continuationToken', continuationToken) 151 | 152 | return parsedUrl.pathname + parsedUrl.search 153 | } -------------------------------------------------------------------------------- /src/provider/achievements.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { AchievementsResponse, AchievementsTitleResponse } from '../types/achievements' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class AchievementsProvider extends BaseProvider { 7 | _endpoint = 'achievements.xboxlive.com' 8 | _headers = { 9 | 'x-xbl-contract-version': '2' 10 | } 11 | 12 | async getAchievements(xuid:string, continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 13 | return (await this.get(this.applyPagination('/users/xuid('+xuid+')/history/titles', maxItems, skipItems, continuationToken))) 14 | } 15 | 16 | async getTitleId(xuid:string, titleId:string, continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 17 | return (await this.get(this.applyPagination('/users/xuid('+xuid+')/achievements?titleid='+titleId, maxItems, skipItems, continuationToken))) 18 | } 19 | 20 | async getItemDetail(xuid:string, serviceConfigId:string, achievementId:string, continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 21 | return (await this.get(this.applyPagination('/users/xuid('+xuid+')/achievements/'+serviceConfigId+'/'+achievementId, maxItems, skipItems, continuationToken))) 22 | } 23 | } -------------------------------------------------------------------------------- /src/provider/base.ts: -------------------------------------------------------------------------------- 1 | import XboxWebApi from '../lib' 2 | import Http, { applyPagination } from '../lib/http' 3 | 4 | export default class BaseProvider { 5 | private readonly _api: XboxWebApi; 6 | private _defaultHeaders 7 | private _disableAuthHeader = false 8 | _endpoint = 'xboxlive.com' 9 | _headers 10 | 11 | constructor(api:XboxWebApi){ 12 | this._api = api 13 | this._defaultHeaders = { 14 | 'Accept-Language': 'en-US', 15 | 'Accept': 'application/json', 16 | 'x-xbl-contract-version': '2', 17 | 'Content-Type': 'application/json' 18 | } 19 | } 20 | 21 | getApi(){ 22 | return this._api 23 | } 24 | 25 | resetDefaultHeaders(){ 26 | this._defaultHeaders = {} 27 | this._disableAuthHeader = true 28 | return true 29 | } 30 | 31 | applyPagination = applyPagination 32 | 33 | async get(path, headers?){ 34 | const _headers = { 35 | ...this._defaultHeaders, 36 | ...this._headers, 37 | ...headers, 38 | ...(this._disableAuthHeader === true) ? {} : { 'Authorization': this.getApi().getAuthorizationHeader() } 39 | } 40 | 41 | const response = await new Http().getRequest(this._endpoint, path, _headers) 42 | return response 43 | } 44 | 45 | async delete(path, data, headers?){ 46 | const _headers = { 47 | ...this._defaultHeaders, 48 | ...this._headers, 49 | ...headers, 50 | ...(this._disableAuthHeader === true) ? {} : { 'Authorization': this.getApi().getAuthorizationHeader() } 51 | } 52 | 53 | const response = await new Http().deleteRequest(this._endpoint, path, _headers) 54 | return response 55 | } 56 | 57 | async post(path, data, headers?){ 58 | const _headers = { 59 | ...this._defaultHeaders, 60 | ...this._headers, 61 | ...headers, 62 | ...(this._disableAuthHeader === true) ? {} : { 'Authorization': this.getApi().getAuthorizationHeader() } 63 | } 64 | 65 | const response = await new Http().postRequest(this._endpoint, path, _headers, data) 66 | return response 67 | } 68 | 69 | async put(path, data, headers?){ 70 | const _headers = { 71 | ...this._defaultHeaders, 72 | ...this._headers, 73 | ...headers, 74 | ...(this._disableAuthHeader === true) ? {} : { 'Authorization': this.getApi().getAuthorizationHeader() } 75 | } 76 | 77 | const response = await new Http().putRequest(this._endpoint, path, _headers, data) 78 | return response 79 | } 80 | } -------------------------------------------------------------------------------- /src/provider/catalog.ts: -------------------------------------------------------------------------------- 1 | import QueryString from 'node:querystring' 2 | import { CatalogSearchResponse } from '../types/catalog' 3 | 4 | import BaseProvider from './base' 5 | import { HttpResponse } from '../lib/http' 6 | 7 | export default class CatalogProvider extends BaseProvider { 8 | _endpoint = 'displaycatalog.mp.microsoft.com' 9 | _headers = { 10 | 'MS-CV': '1.0', 11 | } 12 | 13 | async searchTitle(query:string, market = 'us', language = 'en-us', continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 14 | const searchParams = { 15 | "languages": language, 16 | "market": market, 17 | "platformdependencyname": 'windows.xbox', 18 | "productFamilyNames": "Games,Apps", 19 | "query": query, 20 | "topProducts": 25, 21 | } 22 | 23 | const queryParams = QueryString.stringify(searchParams) 24 | this.resetDefaultHeaders() 25 | 26 | return (await this.get(this.applyPagination('/v7.0/productFamilies/autosuggest?'+queryParams, maxItems, skipItems, continuationToken))) 27 | } 28 | 29 | async getProductId(query:string, market = 'us', language = 'en-us', continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 30 | const searchParams = { 31 | "actionFilter": 'Browse', 32 | "bigIds": [query], 33 | "fieldsTemplate": 'details', 34 | "languages": language, 35 | "market": market, 36 | } 37 | 38 | const queryParams = QueryString.stringify(searchParams) 39 | this.resetDefaultHeaders() 40 | 41 | return (await this.get(this.applyPagination('/v7.0/productFamilies/autosuggest?'+queryParams, maxItems, skipItems, continuationToken))) 42 | } 43 | 44 | async getProductFromAlternateId(titleId:string, titleType:string, market = 'us', language = 'en-us', continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 45 | const searchParams = { 46 | "top": 25, 47 | "alternateId": titleType, 48 | "fieldsTemplate": 'details', 49 | // "languages": 'en-US', 50 | // "market": 'US', 51 | "languages": language, 52 | "market": market, 53 | "value": titleId, 54 | } 55 | 56 | const queryParams = QueryString.stringify(searchParams) 57 | this.resetDefaultHeaders() 58 | 59 | return (await this.get(this.applyPagination('/v7.0/productFamilies/autosuggest?'+queryParams, maxItems, skipItems, continuationToken))) 60 | } 61 | } -------------------------------------------------------------------------------- /src/provider/gameclips.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { GameclipsResponse } from '../types/gameclips' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class GameclipsProvider extends BaseProvider { 7 | _endpoint = 'mediahub.xboxlive.com' 8 | 9 | _headers = { 10 | 'x-xbl-contract-version': '3' 11 | } 12 | 13 | async getGameclips(xuid:string, continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 14 | return (await this.post(this.applyPagination('/gameclips/search', maxItems, skipItems, continuationToken), 15 | { 16 | query: "OwnerXuid eq "+xuid, 17 | max: maxItems, 18 | skip: skipItems 19 | } 20 | )) 21 | } 22 | } -------------------------------------------------------------------------------- /src/provider/gamepass.ts: -------------------------------------------------------------------------------- 1 | import QueryString from 'node:querystring' 2 | import pkg from '../../package.json' 3 | import { SiglResponse, ProductsResponse } from '../types/gamepass' 4 | 5 | import BaseProvider from './base' 6 | import { HttpResponse } from '../lib/http' 7 | 8 | export default class GamepassProvider extends BaseProvider { 9 | _endpoint = 'catalog.gamepass.com' 10 | _headers = { 11 | 'MS-CV': '1.0', 12 | 'calling-app-name': 'github.com/unknownskl/xbox-webapi-node', 13 | 'calling-app-version': pkg.version, 14 | } 15 | 16 | async getSigl(siglId:string, market = 'us', language = 'en-us', continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 17 | const searchParams = { 18 | "id": siglId, 19 | "market": market, 20 | "language": language, 21 | } 22 | const queryParams = QueryString.stringify(searchParams) 23 | return (await this.get('/sigls/v2?'+queryParams)) 24 | } 25 | 26 | async getProducts(products:string[], market = 'us', language = 'en-us', continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 27 | const searchParams = { 28 | "hydration": 'MobileLowAmber0', 29 | "market": market, 30 | "language": language, 31 | } 32 | const queryParams = QueryString.stringify(searchParams) 33 | return (await this.post('/v3/products?'+queryParams,{ 34 | Products: products 35 | })) 36 | } 37 | 38 | async getProductsDetailed(products:string[], market = 'us', language = 'en-us', continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 39 | if(products.length > 20){ 40 | throw new Error('Maximum of 20 products can be requested at a time') 41 | } 42 | 43 | const searchParams = { 44 | "hydration": 'RemoteHighSapphire0', 45 | "market": market, 46 | "language": language, 47 | } 48 | const queryParams = QueryString.stringify(searchParams) 49 | return (await this.post('/v3/products?'+queryParams,{ 50 | Products: products 51 | })) 52 | } 53 | } -------------------------------------------------------------------------------- /src/provider/messages.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { ConversationResponse, InboxResponse } from '../types/messages' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class MessagesProvider extends BaseProvider { 7 | _endpoint = 'xblmessaging.xboxlive.com' 8 | 9 | async getInbox(): Promise> { 10 | return (await this.get('/network/Xbox/users/me/inbox')) 11 | } 12 | 13 | async getConversation(xuid:string, continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 14 | return (await this.get(this.applyPagination('/network/Xbox/users/me/conversations/users/xuid('+xuid+')', maxItems, skipItems, continuationToken))) 15 | } 16 | } -------------------------------------------------------------------------------- /src/provider/people.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { PeopleResponse } from '../types/people' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class PeopleProvider extends BaseProvider { 7 | _endpoint = 'peoplehub.xboxlive.com' 8 | 9 | async getFriends(): Promise> { 10 | const params = [ 11 | 'preferredcolor', 12 | 'detail', 13 | 'multiplayersummary', 14 | 'presencedetail', 15 | ] 16 | return (await this.get('/users/me/people/social/decoration/'+params.join(','))) 17 | } 18 | 19 | async recentPlayers(continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 20 | return (await this.get(this.applyPagination('/users/me/people/recentplayers', maxItems, skipItems, continuationToken))) 21 | } 22 | } -------------------------------------------------------------------------------- /src/provider/pins.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { PinsResponse } from '../types/pins' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class PinsProvider extends BaseProvider { 7 | _endpoint = 'eplists.xboxlive.com' 8 | 9 | async getPins(xuid:string, listname = 'XBLPins'): Promise> { 10 | return (await this.get('/users/xuid('+xuid+')/lists/PINS/'+listname)) 11 | } 12 | 13 | async getSavedForLater(xuid:string): Promise> { 14 | return (await this.getPins(xuid, 'SaveForLater')) 15 | } 16 | } -------------------------------------------------------------------------------- /src/provider/profile.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { ProfileResponse } from '../types/profile' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class ProfileProvider extends BaseProvider { 7 | _endpoint = 'profile.xboxlive.com' 8 | _headers = { 9 | 'x-xbl-contract-version': 3 10 | } 11 | 12 | async getCurrentUser(): Promise> { 13 | return (await this.get('/users/me/profile/settings?settings=GameDisplayName,GameDisplayPicRaw,Gamerscore,Gamertag')) 14 | } 15 | 16 | async getUserProfile(xuid:string): Promise> { 17 | return (await this.get('/users/xuid('+xuid+')/profile/settings?settings=GameDisplayName,GameDisplayPicRaw,Gamerscore,Gamertag')) 18 | } 19 | 20 | async getByGamertag(gamertag:string): Promise> { 21 | return (await this.get('/users/gt('+gamertag+')/profile/settings?settings=GameDisplayName,GameDisplayPicRaw,Gamerscore,Gamertag')) 22 | } 23 | } -------------------------------------------------------------------------------- /src/provider/rest.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { URL } from 'url' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class RestProvider extends BaseProvider { 7 | _endpoint = 'xboxlive.com' 8 | _headers = {} 9 | 10 | async getRequest(url:string, xblContractVersion:undefined|number = undefined, continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise { 11 | if(! URL.canParse(url)){ 12 | throw new Error('Invalid URL supplied') 13 | } 14 | 15 | const parsedUrl = new URL(url) 16 | this._endpoint = parsedUrl.hostname 17 | 18 | if(xblContractVersion !== undefined){ 19 | this._headers['x-xbl-contract-version'] = xblContractVersion.toString() 20 | } 21 | 22 | return (await this.get(this.applyPagination(parsedUrl.pathname + parsedUrl.search, maxItems, skipItems, continuationToken))) 23 | } 24 | } -------------------------------------------------------------------------------- /src/provider/screenshots.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import BaseProvider from './base' 3 | import { ScreenshotsResponse } from '../types/screenshots' 4 | 5 | export default class ScreenshotsProvider extends BaseProvider { 6 | _endpoint = 'mediahub.xboxlive.com' 7 | 8 | _headers = { 9 | 'x-xbl-contract-version': '3' 10 | } 11 | 12 | async getScreenshots(xuid:string, continuationToken:undefined|string = undefined, maxItems = undefined, skipItems = undefined): Promise> { 13 | return (await this.post(this.applyPagination('/screenshots/search', maxItems, skipItems, continuationToken), 14 | { 15 | query: "OwnerXuid eq "+xuid, 16 | max: maxItems, 17 | skip: skipItems 18 | } 19 | )) 20 | } 21 | } -------------------------------------------------------------------------------- /src/provider/smartglass.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { SmartglassResponse, Console, ConsoleStatus, App, StorageDevice } from '../types/smartglass' 3 | 4 | import BaseProvider from './base' 5 | import Uuid4 from 'uuid4' 6 | 7 | export default class SmartglassProvider extends BaseProvider { 8 | _endpoint = 'xccs.xboxlive.com' 9 | _headers = { 10 | 'x-xbl-contract-version': '4', 11 | 'skillplatform': 'RemoteManagement' 12 | } 13 | 14 | async getConsolesList(): Promise>> { 15 | return (await this.get('/lists/devices?queryCurrentDevice=false&includeStorageDevices=true')) 16 | } 17 | 18 | async getInstalledApps(consoleId:string): Promise>> { 19 | return (await this.get('/lists/installedApps?deviceId='+consoleId)) 20 | } 21 | 22 | async getStorageDevices(consoleId:string): Promise>> { 23 | return (await this.get('/lists/storageDevices?deviceId='+consoleId)) 24 | } 25 | 26 | async getConsoleStatus(consoleId:string): Promise> { 27 | return (await this.get('/consoles/'+consoleId)) 28 | } 29 | 30 | 31 | 32 | 33 | async powerOn(consoleId?:string) { 34 | return this._sendCommand(consoleId, 'Power', 'WakeUp') 35 | } 36 | 37 | async powerOff(consoleId?:string) { 38 | return this._sendCommand(consoleId, 'Power', 'TurnOff') 39 | } 40 | 41 | async launchOneGuide(consoleId:string) { 42 | return this._sendCommand(consoleId, 'TV', 'ShowGuide') 43 | } 44 | 45 | _sendCommand(consoleId, commandType, command, params?){ 46 | if(params == undefined){ 47 | params = [] 48 | } 49 | 50 | const session_id = Uuid4() 51 | // var session_id = '2c1f6eae-30f1-4b03-81bf-ff11f4e02079' 52 | 53 | const postParams = { 54 | "destination": "Xbox", 55 | "type": commandType, 56 | "command": command, 57 | "sessionId": session_id, 58 | "sourceId": "com.microsoft.smartglass", 59 | "parameters": params, 60 | "linkedXboxId": consoleId, 61 | } 62 | 63 | const postData = JSON.stringify(postParams) 64 | 65 | return this.post('/commands', postData) 66 | } 67 | } -------------------------------------------------------------------------------- /src/provider/social.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { SummaryResponse } from '../types/social' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class SocialProvider extends BaseProvider { 7 | _endpoint = 'social.xboxlive.com' 8 | 9 | async getSummary(): Promise> { 10 | return (await this.get('/users/me/summary')) 11 | } 12 | 13 | async getSummaryByXuid(xuid:string): Promise> { 14 | return (await this.get('/users/xuid('+xuid+')/summary')) 15 | } 16 | 17 | async getSummaryByGamertag(gamertag:string): Promise> { 18 | return (await this.get('/users/gt('+gamertag+')/summary')) 19 | } 20 | } -------------------------------------------------------------------------------- /src/provider/titlehub.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { TitleHistoryResponse } from '../types/titlehub' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class TitlehubProvider extends BaseProvider { 7 | _endpoint = 'titlehub.xboxlive.com' 8 | 9 | async getTitleHistory(xuid:string): Promise> { 10 | const params = [ 11 | 'achievement', 12 | 'image', 13 | 'scid', 14 | ] 15 | return (await this.get('/users/xuid('+xuid+')/titles/titlehistory/decoration/'+params.join(','))) 16 | } 17 | 18 | async getTitleId(xuid:string, titleId:string): Promise> { 19 | 20 | const params = [ 21 | 'achievement', 22 | 'image', 23 | 'detail', 24 | 'scid', 25 | 'alternateTitleId' 26 | ] 27 | return (await this.get('/users/xuid('+xuid+')/titles/titleid('+titleId+')/decoration/'+params.join(','))) 28 | } 29 | } -------------------------------------------------------------------------------- /src/provider/userpresence.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { UserResponse } from '../types/userpresence' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class UserpresenceProvider extends BaseProvider { 7 | _endpoint = 'userpresence.xboxlive.com' 8 | _headers = { 9 | 'x-xbl-contract-version': '3' 10 | } 11 | 12 | async getCurrentUser(): Promise> { 13 | return (await this.get('/users/me?level=all')) 14 | } 15 | 16 | async getFriends(): Promise> { 17 | return (await this.get('/users/me/groups/People?level=all')) 18 | } 19 | 20 | async getUser(xuid:string): Promise> { 21 | return (await this.get('/users/xuid('+xuid+')?level=all')) 22 | } 23 | } -------------------------------------------------------------------------------- /src/provider/usersearch.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { UsersearchResponse } from '../types/usersearch' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class UsersearchProvider extends BaseProvider { 7 | _endpoint = 'usersearch.xboxlive.com' 8 | _headers = { 9 | 'x-xbl-contract-version': '1' 10 | } 11 | 12 | async searchUsers(query:string): Promise> { 13 | return (await this.get('/suggest?q='+query)) 14 | } 15 | } -------------------------------------------------------------------------------- /src/provider/userstats.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { UserstatResponse } from '../types/userstats' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class UserstatsProvider extends BaseProvider { 7 | _endpoint = 'userstats.xboxlive.com' 8 | _headers = { 9 | 'x-xbl-contract-version': '2' 10 | } 11 | 12 | async getUserTitleStats(xuid:string, titleId:string): Promise> { 13 | return (await this.post('/batch', 14 | { 15 | "arrangebyfield":"xuid", 16 | "xuids":[ 17 | xuid 18 | ], 19 | "groups":[ 20 | { 21 | "name":"Hero", 22 | "titleId": titleId 23 | } 24 | ], 25 | "stats":[ 26 | { 27 | "name":"MinutesPlayed", 28 | "titleId": titleId 29 | } 30 | ] 31 | } 32 | )) 33 | } 34 | } -------------------------------------------------------------------------------- /src/provider/xnotify.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../lib/http' 2 | import { StatusResponse } from '../types/xnotify' 3 | 4 | import BaseProvider from './base' 5 | 6 | export default class XnotifyProvider extends BaseProvider { 7 | _endpoint = 'xnotify.xboxlive.com' 8 | 9 | async getLiveStatus(): Promise> { 10 | return (await this.get('/servicestatusv6/GB/en-GB')) 11 | } 12 | } -------------------------------------------------------------------------------- /src/types/achievements/index.ts: -------------------------------------------------------------------------------- 1 | import { PagingInfo } from '../paging' 2 | 3 | export interface AchievementsResponse { 4 | titles: Title[]; 5 | pagingInfo: PagingInfo; 6 | } 7 | 8 | export interface Title { 9 | lastUnlock: Date; 10 | titleId: number; 11 | serviceConfigId: string; 12 | titleType: TitleType; 13 | platform: Platform; 14 | name: string; 15 | earnedAchievements: number; 16 | currentGamerscore: number; 17 | maxGamerscore: number; 18 | } 19 | 20 | export enum Platform { 21 | Durango = "Durango", 22 | WindowsOneCore = "WindowsOneCore", 23 | XboxOne = "XboxOne", 24 | } 25 | 26 | export enum TitleType { 27 | DGame = "DGame", 28 | Game = "Game", 29 | LiveApp = "LiveApp", 30 | } 31 | 32 | // TitleResponse 33 | 34 | export interface AchievementsTitleResponse { 35 | achievements: Achievement[]; 36 | pagingInfo: PagingInfo; 37 | } 38 | 39 | export interface Achievement { 40 | id: string; 41 | serviceConfigId: string; 42 | name: string; 43 | titleAssociations: TitleAssociation[]; 44 | progressState: ProgressState; 45 | progression: Progression; 46 | mediaAssets: MediaAsset[]; 47 | platforms: Platform[]; 48 | isSecret: boolean; 49 | description: string; 50 | lockedDescription: string; 51 | productId: string; 52 | achievementType: AchievementType; 53 | participationType: ParticipationType; 54 | timeWindow: null; 55 | rewards: Reward[]; 56 | estimatedTime: string; 57 | deeplink: string; 58 | isRevoked: boolean; 59 | } 60 | 61 | export enum AchievementType { 62 | Persistent = "Persistent", 63 | } 64 | 65 | export interface MediaAsset { 66 | name: string; 67 | type: MediaAssetType; 68 | url: string; 69 | } 70 | 71 | export enum MediaAssetType { 72 | Icon = "Icon", 73 | } 74 | 75 | export enum ParticipationType { 76 | Individual = "Individual", 77 | } 78 | 79 | export enum ProgressState { 80 | NotStarted = "NotStarted", 81 | Achieved = "Achieved", 82 | } 83 | 84 | export interface Progression { 85 | requirements: Requirement[]; 86 | timeUnlocked: Date; 87 | } 88 | 89 | export interface Requirement { 90 | id: string; 91 | current: null; 92 | target: string; 93 | operationType: OperationType; 94 | valueType: RequirementValueType; 95 | ruleParticipationType: ParticipationType; 96 | } 97 | 98 | export enum OperationType { 99 | Sum = "SUM", 100 | } 101 | 102 | export enum RequirementValueType { 103 | Integer = "Integer", 104 | } 105 | 106 | export interface Reward { 107 | name: null; 108 | description: null; 109 | value: string; 110 | type: RewardType; 111 | mediaAsset: null; 112 | valueType: RewardValueType; 113 | } 114 | 115 | export enum RewardType { 116 | Gamerscore = "Gamerscore", 117 | } 118 | 119 | export enum RewardValueType { 120 | Int = "Int", 121 | } 122 | 123 | export interface TitleAssociation { 124 | name: Name; 125 | id: number; 126 | } 127 | 128 | export enum Name { 129 | Cuphead = "Cuphead", 130 | } -------------------------------------------------------------------------------- /src/types/catalog/index.ts: -------------------------------------------------------------------------------- 1 | export interface CatalogSearchResponse { 2 | Results: Result[]; 3 | TotalResultCount: number; 4 | } 5 | 6 | export interface Result { 7 | ProductFamilyName: ProductFamilyName; 8 | Products: Product[]; 9 | } 10 | 11 | export enum ProductFamilyName { 12 | Apps = "Apps", 13 | Games = "Games", 14 | } 15 | 16 | export interface Product { 17 | BackgroundColor: string; 18 | Height: number; 19 | ImageType: ImageType; 20 | Width: number; 21 | PlatformProperties: any[]; 22 | Icon: string; 23 | ProductId: string; 24 | Type: Type; 25 | Title: string; 26 | } 27 | 28 | export enum ImageType { 29 | BoxArt = "BoxArt", 30 | Logo = "Logo", 31 | Tile = "Tile", 32 | } 33 | 34 | export enum Type { 35 | Application = "Application", 36 | Durable = "Durable", 37 | Game = "Game", 38 | } 39 | -------------------------------------------------------------------------------- /src/types/gameclips/index.ts: -------------------------------------------------------------------------------- 1 | import { PagingInfo } from '../paging' 2 | 3 | export interface GameclipsResponse { 4 | values: Value[]; 5 | } 6 | 7 | export interface Value { 8 | contentId: string; 9 | contentLocators: ContentLocator[]; 10 | contentSegments: ContentSegment[]; 11 | creationType: string; 12 | durationInSeconds: number; 13 | expirationDate: Date; 14 | frameRate: number; 15 | greatestMomentId: string; 16 | localId: string; 17 | ownerXuid: number; 18 | resolutionHeight: number; 19 | resolutionWidth: number; 20 | sandboxId: string; 21 | sharedTo: any[]; 22 | titleData: string; 23 | titleId: number; 24 | titleName: string; 25 | uploadDate: Date; 26 | uploadLanguage: string; 27 | uploadRegion: string; 28 | uploadTitleId: number; 29 | uploadDeviceType: string; 30 | userCaption: string; 31 | commentCount: number; 32 | likeCount: number; 33 | shareCount: number; 34 | viewCount: number; 35 | contentState: string; 36 | enforcementState: string; 37 | safetyThreshold: string; 38 | sessions: any[]; 39 | tournaments: any[]; 40 | } 41 | 42 | export interface ContentLocator { 43 | expiration?: Date; 44 | fileSize?: number; 45 | locatorType: string; 46 | uri: string; 47 | } 48 | 49 | export interface ContentSegment { 50 | segmentId: number; 51 | creationType: string; 52 | creatorChannelId: null; 53 | creatorXuid: number; 54 | recordDate: Date; 55 | durationInSeconds: number; 56 | offset: number; 57 | secondaryTitleId: null; 58 | titleId: number; 59 | } 60 | -------------------------------------------------------------------------------- /src/types/gamepass/index.ts: -------------------------------------------------------------------------------- 1 | export type SiglResponse = [SiglHeader, ...SiglProduct[]]; 2 | 3 | export interface SiglHeader { 4 | siglId: string; 5 | title: string; 6 | description: string; 7 | requiresShuffling: string; 8 | imageUrl: string; 9 | } 10 | 11 | export interface SiglProduct { 12 | id: string; 13 | } 14 | 15 | 16 | export interface ProductsResponse { 17 | Products: { [key: string]: Product }; 18 | InvalidIds: any[]; 19 | } 20 | 21 | export interface Product { 22 | alternateIds: AlternateID[]; 23 | productKind: ProductKind; 24 | name: string; 25 | developerName?: string; 26 | publisherName: string; 27 | tileImage: Image; 28 | posterImage: Image; 29 | heroImage?: Image; 30 | releaseDate: Date; 31 | consoleComingSoonDate?: Date; 32 | gamePassStandardComingSoonDate?: Date; 33 | isEAPlay: boolean; 34 | xCloudIsStreamable: boolean; 35 | xCloudSupportsTouch: boolean; 36 | xCloudPrograms?: XCloudProgram[]; 37 | capabilities?: Capability[]; 38 | categories: Category[]; 39 | availablePlatforms: AvailablePlatform[]; 40 | isBundle: boolean; 41 | StoreId: string; 42 | pcComingSoonDate?: Date; 43 | eaConsoleComingSoonDate?: Date; 44 | gamePassCoreComingSoonDate?: Date; 45 | consoleCrossGenSiblings?: string[]; 46 | gameCatalogExtensionId?: string; 47 | } 48 | 49 | export interface AlternateID { 50 | idType: IDType; 51 | id: string[] | string; 52 | } 53 | 54 | export enum IDType { 55 | Childxboxtitleids = "CHILDXBOXTITLEIDS", 56 | Xboxtitleid = "XBOXTITLEID", 57 | Xcloudtitleid = "XCLOUDTITLEID", 58 | } 59 | 60 | export enum AvailablePlatform { 61 | Hub = "Hub", 62 | MobileDevice = "MobileDevice", 63 | PC = "PC", 64 | XCloud = "XCloud", 65 | XboxOne = "XboxOne", 66 | XboxSeriesX = "XboxSeriesX", 67 | } 68 | 69 | export interface Capability { 70 | id: IDEnum; 71 | name: string; 72 | applicablePlatforms?: ApplicablePlatform[]; 73 | } 74 | 75 | export enum ApplicablePlatform { 76 | Desktop = "Desktop", 77 | Xbox = "Xbox", 78 | } 79 | 80 | export enum IDEnum { 81 | Capability4K = "Capability4k", 82 | CapabilityHDR = "CapabilityHDR", 83 | CapabilityVRR = "CapabilityVRR", 84 | CapabilityXboxEnhanced = "CapabilityXboxEnhanced", 85 | ConsoleCrossGen = "ConsoleCrossGen", 86 | ConsoleGen9Optimized = "ConsoleGen9Optimized", 87 | ConsoleKeyboardMouse = "ConsoleKeyboardMouse", 88 | CoopSupportLocal = "CoopSupportLocal", 89 | CoopSupportOnline = "CoopSupportOnline", 90 | DolbyAtmos = "DolbyAtmos", 91 | Dtsx = "DTSX", 92 | FPSBoostEnabledX = "FPSBoostEnabledX", 93 | FPSBoostEnabledXS = "FPSBoostEnabledXS", 94 | GameStreaming = "GameStreaming", 95 | LocalMultiplayer = "LocalMultiplayer", 96 | OnlineMultiplayerWithGold = "OnlineMultiplayerWithGold", 97 | PCGamePad = "PcGamePad", 98 | RayTracing = "RayTracing", 99 | SharedSplitScreen = "SharedSplitScreen", 100 | SinglePlayer = "SinglePlayer", 101 | SpatialSound = "SpatialSound", 102 | The120FPS = "120fps", 103 | The60FPS = "60fps", 104 | XblAchievements = "XblAchievements", 105 | XblCloudSaves = "XblCloudSaves", 106 | XblClubs = "XblClubs", 107 | XblCrossPlatformCoop = "XblCrossPlatformCoop", 108 | XblCrossPlatformMultiPlayer = "XblCrossPlatformMultiPlayer", 109 | XblLocalCoop = "XblLocalCoop", 110 | XblLocalMultiPlayer = "XblLocalMultiPlayer", 111 | XblOnlineCoop = "XblOnlineCoop", 112 | XblOnlineMultiPlayer = "XblOnlineMultiPlayer", 113 | XblPresence = "XblPresence", 114 | XboxLiveCrossGenMP = "XboxLiveCrossGenMP", 115 | Xpa = "XPA", 116 | } 117 | 118 | export enum Category { 119 | ActionAdventure = "Action & adventure", 120 | CardBoard = "Card & board", 121 | Classics = "Classics", 122 | FamilyKids = "Family & kids", 123 | Fighting = "Fighting", 124 | MultiPlayerOnlineBattleArena = "Multi-Player Online Battle Arena", 125 | Other = "Other", 126 | Platformer = "Platformer", 127 | PuzzleTrivia = "Puzzle & trivia", 128 | RacingFlying = "Racing & flying", 129 | RolePlaying = "Role playing", 130 | Shooter = "Shooter", 131 | Simulation = "Simulation", 132 | Sports = "Sports", 133 | Strategy = "Strategy", 134 | Word = "Word", 135 | } 136 | 137 | export interface Image { 138 | uri: string; 139 | height: number; 140 | width: number; 141 | } 142 | 143 | export enum ProductKind { 144 | Game = "GAME", 145 | } 146 | 147 | export enum XCloudProgram { 148 | Byog = "BYOG", 149 | Gpultimate = "GPULTIMATE", 150 | } 151 | -------------------------------------------------------------------------------- /src/types/messages/index.ts: -------------------------------------------------------------------------------- 1 | // Inbox 2 | export interface InboxResponse { 3 | primary: Primary; 4 | folders: Primary[]; 5 | safetySettings: SafetySettings; 6 | } 7 | 8 | export interface Primary { 9 | folder: string; 10 | totalCount: number; 11 | unreadCount: number; 12 | conversations?: Conversation[]; 13 | } 14 | 15 | export interface Conversation { 16 | timestamp: Date; 17 | networkId: NetworkID; 18 | type: ConversationTypeEnum; 19 | conversationId: string; 20 | participants: string[]; 21 | folder: Folder; 22 | readHorizon: string; 23 | deleteHorizon: string; 24 | lastMessage: LastMessage; 25 | notificationOptions: NotificationOptions; 26 | isRead: boolean; 27 | directMentionHorizon: string; 28 | } 29 | 30 | export enum Folder { 31 | Primary = "Primary", 32 | } 33 | 34 | export interface LastMessage { 35 | contentPayload: ContentPayload; 36 | timestamp: Date; 37 | lastUpdateTimestamp: Date; 38 | type: PurpleType; 39 | networkId: NetworkID; 40 | conversationType: ConversationTypeEnum; 41 | conversationId: string; 42 | owner?: number; 43 | sender: string; 44 | messageId: string; 45 | clock: string; 46 | isDeleted: boolean; 47 | isServerUpdated: boolean; 48 | } 49 | 50 | export interface ContentPayload { 51 | content: Content; 52 | } 53 | 54 | export interface Content { 55 | parts: Part[]; 56 | } 57 | 58 | export interface Part { 59 | contentType: ContentType; 60 | version: number; 61 | text?: string; 62 | voiceAttachmentId?: string; 63 | duration?: number; 64 | } 65 | 66 | export enum NotificationOptions { 67 | Smart = "Smart", 68 | } 69 | 70 | export interface SafetySettings { 71 | version: number; 72 | primaryInboxMedia: string; 73 | primaryInboxText: string; 74 | primaryInboxUrl: string; 75 | secondaryInboxMedia: string; 76 | secondaryInboxText: string; 77 | secondaryInboxUrl: string; 78 | canUnobscure: boolean; 79 | } 80 | 81 | 82 | 83 | // Conversations 84 | export interface ConversationResponse { 85 | timestamp: Date; 86 | networkId: NetworkID; 87 | type: ConversationTypeEnum; 88 | conversationId: string; 89 | participants: string[]; 90 | readHorizon: string; 91 | deleteHorizon: string; 92 | isRead: boolean; 93 | folder: string; 94 | notificationOptions: string; 95 | messages: Message[]; 96 | continuationToken: null; 97 | directMentionHorizon: string; 98 | muted: boolean; 99 | voiceId: string; 100 | voiceRoster: any[]; 101 | } 102 | 103 | export interface Message { 104 | contentPayload: ContentPayload; 105 | timestamp: Date; 106 | lastUpdateTimestamp: Date; 107 | type: PurpleType; 108 | networkId: NetworkID; 109 | conversationType: ConversationTypeEnum; 110 | conversationId: string; 111 | sender: string; 112 | messageId: string; 113 | clock: string; 114 | isDeleted: boolean; 115 | isServerUpdated: boolean; 116 | } 117 | 118 | export interface ContentPayload { 119 | content: Content; 120 | } 121 | 122 | export interface Content { 123 | parts: Part[]; 124 | } 125 | 126 | export interface Part { 127 | contentType: ContentType; 128 | text?: string; 129 | version: number; 130 | buttonText?: string; 131 | appUri?: null | string; 132 | code?: string; 133 | webUri?: null | string; 134 | } 135 | 136 | export enum ContentType { 137 | Deeplink = "deeplink", 138 | Fivebyfive = "fivebyfive", 139 | Text = "text", 140 | Voice = "voice", 141 | } 142 | 143 | export enum ConversationTypeEnum { 144 | OneToOne = "OneToOne", 145 | } 146 | 147 | export enum NetworkID { 148 | Xbox = "Xbox", 149 | } 150 | 151 | export enum PurpleType { 152 | ContentMessage = "ContentMessage", 153 | } 154 | -------------------------------------------------------------------------------- /src/types/paging.ts: -------------------------------------------------------------------------------- 1 | export interface PagingInfo { 2 | continuationToken: string; 3 | totalRecords: number; 4 | } -------------------------------------------------------------------------------- /src/types/people/index.ts: -------------------------------------------------------------------------------- 1 | export interface PeopleResponse { 2 | people: Person[]; 3 | recommendationSummary: null; 4 | friendFinderState: null; 5 | } 6 | 7 | export interface Person { 8 | xuid: string; 9 | isFavorite: boolean; 10 | isFollowingCaller: boolean; 11 | isFollowedByCaller: boolean; 12 | isIdentityShared: boolean; 13 | addedDateTimeUtc: Date; 14 | displayName: string; 15 | realName: string; 16 | displayPicRaw: string; 17 | showUserAsAvatar: string; 18 | gamertag: string; 19 | gamerScore: string; 20 | xboxOneRep: XboxOneRep; 21 | presenceState: PresenceState; 22 | presenceText: string; 23 | presenceDevices: null; 24 | isBroadcasting: boolean; 25 | isCloaked: null; 26 | isQuarantined: boolean; 27 | suggestion: null; 28 | recommendation: null; 29 | titleHistory: null; 30 | multiplayerSummary: MultiplayerSummary; 31 | recentPlayer: null; 32 | follower: null; 33 | preferredColor: PreferredColor; 34 | presenceDetails: PresenceDetail[]; 35 | titlePresence: null; 36 | titleSummaries: null; 37 | presenceTitleIds: null; 38 | detail: Detail; 39 | communityManagerTitles: null; 40 | socialManager: null; 41 | broadcast: null; 42 | tournamentSummary: null; 43 | avatar: null; 44 | } 45 | 46 | export interface Detail { 47 | accountTier: AccountTier; 48 | bio: string; 49 | isVerified: boolean; 50 | location: string; 51 | tenure: string; 52 | watermarks: string[]; 53 | blocked: boolean; 54 | mute: boolean; 55 | followerCount: number; 56 | followingCount: number; 57 | } 58 | 59 | export enum AccountTier { 60 | Gold = "Gold", 61 | Silver = "Silver", 62 | } 63 | 64 | export interface MultiplayerSummary { 65 | InMultiplayerSession: number; 66 | InParty: number; 67 | } 68 | 69 | export interface PreferredColor { 70 | primaryColor: string; 71 | secondaryColor: string; 72 | tertiaryColor: string; 73 | } 74 | 75 | export interface PresenceDetail { 76 | IsBroadcasting: boolean; 77 | Device: string; 78 | PresenceText: string; 79 | State: State; 80 | TitleId: string; 81 | TitleType: null; 82 | IsPrimary: boolean; 83 | IsGame: boolean; 84 | RichPresenceText: null | string; 85 | } 86 | 87 | export enum State { 88 | Active = "Active", 89 | LastSeen = "LastSeen", 90 | } 91 | 92 | export enum PresenceState { 93 | Offline = "Offline", 94 | Online = "Online", 95 | } 96 | 97 | export enum XboxOneRep { 98 | GoodPlayer = "GoodPlayer", 99 | } 100 | -------------------------------------------------------------------------------- /src/types/pins/index.ts: -------------------------------------------------------------------------------- 1 | export interface PinsResponse { 2 | ImpressionId: string; 3 | ListItems: ListItem[]; 4 | ListMetadata: ListMetadata; 5 | } 6 | 7 | export interface ListItem { 8 | DateAdded: string; 9 | DateModified: string; 10 | Index: number; 11 | KValue: number; 12 | Item: Item; 13 | } 14 | 15 | export interface Item { 16 | ContentType: ContentType; 17 | ItemId: string; 18 | Title: string; 19 | DeviceType: DeviceType; 20 | Provider: null; 21 | ProviderId: null; 22 | } 23 | 24 | export enum ContentType { 25 | DApp = "DApp", 26 | DGame = "DGame", 27 | } 28 | 29 | export enum DeviceType { 30 | XboxOne = "XboxOne", 31 | } 32 | 33 | export interface ListMetadata { 34 | ListTitle: string; 35 | ListVersion: number; 36 | ListCount: number; 37 | AllowDuplicates: boolean; 38 | MaxListSize: number; 39 | AccessSetting: string; 40 | } 41 | -------------------------------------------------------------------------------- /src/types/profile/index.ts: -------------------------------------------------------------------------------- 1 | export interface ProfileResponse { 2 | profileUsers: ProfileUser[]; 3 | } 4 | 5 | export interface ProfileUser { 6 | id: string; 7 | hostId: string; 8 | settings: Setting[]; 9 | isSponsoredUser: boolean; 10 | } 11 | 12 | export interface Setting { 13 | id: string; 14 | value: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/types/screenshots/index.ts: -------------------------------------------------------------------------------- 1 | export interface ScreenshotsResponse { 2 | values: Value[]; 3 | } 4 | 5 | export interface Value { 6 | captureDate: Date; 7 | contentId: string; 8 | contentLocators: ContentLocator[]; 9 | CreationType: string; 10 | expirationDate: Date; 11 | localId: string; 12 | ownerXuid: number; 13 | resolutionHeight: number; 14 | resolutionWidth: number; 15 | sandboxId: string; 16 | sharedTo: any[]; 17 | titleId: number; 18 | titleName: string; 19 | dateUploaded: Date; 20 | uploadLanguage: string; 21 | uploadRegion: string; 22 | uploadTitleId: number; 23 | uploadDeviceType: string; 24 | commentCount: number; 25 | likeCount: number; 26 | shareCount: number; 27 | viewCount: number; 28 | contentState: string; 29 | enforcementState: string; 30 | safetyThreshold: string; 31 | sessions: any[]; 32 | tournaments: any[]; 33 | } 34 | 35 | export interface ContentLocator { 36 | fileSize?: number; 37 | locatorType: string; 38 | uri: string; 39 | } 40 | -------------------------------------------------------------------------------- /src/types/smartglass/index.ts: -------------------------------------------------------------------------------- 1 | export interface SmartglassResponse { 2 | status: Status; 3 | result: T[]; 4 | agentUserId: null; 5 | } 6 | 7 | export interface Console { 8 | id: string; 9 | name: string; 10 | locale: string; 11 | region: string; 12 | consoleType: string; 13 | powerState: string; 14 | digitalAssistantRemoteControlEnabled: boolean; 15 | remoteManagementEnabled: boolean; 16 | consoleStreamingEnabled: boolean; 17 | wirelessWarning: boolean; 18 | outOfHomeWarning: boolean; 19 | storageDevices: StorageDevice[]; 20 | } 21 | 22 | export interface App { 23 | oneStoreProductId: null | string; 24 | titleId: number; 25 | aumid: null | string; 26 | lastActiveTime: Date | null; 27 | isGame: boolean; 28 | name: string; 29 | contentType: ContentType; 30 | instanceId: string; 31 | storageDeviceId: string; 32 | uniqueId: string; 33 | legacyProductId: null | string; 34 | version: number; 35 | sizeInBytes: number; 36 | installTime: Date; 37 | updateTime: Date | null; 38 | parentId: string | null; 39 | } 40 | 41 | export interface StorageDevice { 42 | storageDeviceId: string; 43 | storageDeviceName: string; 44 | isDefault: boolean; 45 | freeSpaceBytes: number; 46 | totalSpaceBytes: number; 47 | isGen9Compatible: null; 48 | } 49 | 50 | export interface Status { 51 | errorCode: string; 52 | errorMessage: null; 53 | } 54 | 55 | export enum ContentType { 56 | App = "App", 57 | Dlc = "Dlc", 58 | Game = "Game", 59 | } 60 | 61 | export interface ConsoleStatus { 62 | status: Status; 63 | id: string; 64 | name: string; 65 | locale: string; 66 | region: string; 67 | consoleType: string; 68 | powerState: string; 69 | playbackState: string; 70 | loginState: null; 71 | focusAppAumid: string; 72 | isTvConfigured: boolean; 73 | digitalAssistantRemoteControlEnabled: boolean; 74 | consoleStreamingEnabled: boolean; 75 | remoteManagementEnabled: boolean; 76 | } -------------------------------------------------------------------------------- /src/types/social/index.ts: -------------------------------------------------------------------------------- 1 | export interface SummaryResponse { 2 | targetFollowingCount: number; 3 | targetFollowerCount: number; 4 | isCallerFollowingTarget: boolean; 5 | isTargetFollowingCaller: boolean; 6 | hasCallerMarkedTargetAsFavorite: boolean; 7 | hasCallerMarkedTargetAsIdentityShared: boolean; 8 | hasCallerMarkedTargetAsKnown: boolean; 9 | legacyFriendStatus: string; 10 | availablePeopleSlots: number; 11 | isFriend: boolean; 12 | availableFollowingSlots: number; 13 | } 14 | -------------------------------------------------------------------------------- /src/types/titlehub/index.ts: -------------------------------------------------------------------------------- 1 | export interface TitleHistoryResponse { 2 | xuid: string; 3 | titles: Title[]; 4 | } 5 | 6 | export interface Title { 7 | titleId: string; 8 | pfn: null | string; 9 | bingId: null | string; 10 | serviceConfigId?: string; 11 | windowsPhoneProductId: null; 12 | name: string; 13 | type: TitleType; 14 | devices: Device[]; 15 | displayImage: null | string; 16 | mediaItemType: MediaItemType; 17 | modernTitleId: null | string; 18 | isBundle: boolean; 19 | achievement: Achievement; 20 | stats: null; 21 | gamePass: null; 22 | images: Image[]; 23 | titleHistory: TitleHistory; 24 | titleRecord: null; 25 | detail: null; 26 | friendsWhoPlayed: null; 27 | alternateTitleIds: null; 28 | contentBoards: null; 29 | xboxLiveTier: XboxLiveTier; 30 | isStreamable?: boolean; 31 | } 32 | 33 | export interface Achievement { 34 | currentAchievements: number; 35 | totalAchievements: number; 36 | currentGamerscore: number; 37 | totalGamerscore: number; 38 | progressPercentage: number; 39 | sourceVersion: number; 40 | } 41 | 42 | export enum Device { 43 | IOS = "iOS", 44 | Mobile = "Mobile", 45 | NintendoSwitch = "Nintendo Switch", 46 | PC = "PC", 47 | Win32 = "Win32", 48 | Xbox360 = "Xbox360", 49 | XboxOne = "XboxOne", 50 | XboxSeries = "XboxSeries", 51 | } 52 | 53 | export interface Image { 54 | url: string; 55 | type: ImageType; 56 | caption: null | string; 57 | } 58 | 59 | export enum ImageType { 60 | BoxArt = "BoxArt", 61 | BrandedKeyArt = "BrandedKeyArt", 62 | FeaturePromotionalSquareArt = "FeaturePromotionalSquareArt", 63 | Hero = "Hero", 64 | Image = "Image", 65 | ImageGallery = "ImageGallery", 66 | Logo = "Logo", 67 | Poster = "Poster", 68 | Screenshot = "Screenshot", 69 | SellImage = "SellImage", 70 | SuperHeroArt = "SuperHeroArt", 71 | Thumbnail = "Thumbnail", 72 | Tile = "Tile", 73 | TitledHeroArt = "TitledHeroArt", 74 | WideBackgroundImage = "WideBackgroundImage", 75 | } 76 | 77 | export enum MediaItemType { 78 | Application = "Application", 79 | Xbox360Game = "Xbox360Game", 80 | XboxArcadeGame = "XboxArcadeGame", 81 | XboxOriginalGame = "XboxOriginalGame", 82 | } 83 | 84 | export interface TitleHistory { 85 | lastTimePlayed: Date; 86 | visible: boolean; 87 | canHide: boolean; 88 | } 89 | 90 | export enum TitleType { 91 | Game = "Game", 92 | } 93 | 94 | export enum XboxLiveTier { 95 | Full = "Full", 96 | None = "None", 97 | Open = "Open", 98 | } 99 | -------------------------------------------------------------------------------- /src/types/userpresence/index.ts: -------------------------------------------------------------------------------- 1 | export interface UserResponse { 2 | xuid: string; 3 | state: State; 4 | lastSeen?: LastSeen; 5 | devices?: Device[]; 6 | } 7 | 8 | export interface Device { 9 | type: string; 10 | titles: Title[]; 11 | } 12 | 13 | export interface Title { 14 | id: string; 15 | name: string; 16 | placement: string; 17 | state: string; 18 | lastModified: Date; 19 | activity?: Activity; 20 | } 21 | 22 | export interface Activity { 23 | richPresence: string; 24 | } 25 | 26 | export interface LastSeen { 27 | deviceType: string; 28 | titleId: string; 29 | titleName: string; 30 | timestamp: Date; 31 | } 32 | 33 | export enum State { 34 | Offline = "Offline", 35 | Online = "Online", 36 | } 37 | -------------------------------------------------------------------------------- /src/types/usersearch/index.ts: -------------------------------------------------------------------------------- 1 | export interface UsersearchResponse { 2 | results: Result[]; 3 | } 4 | 5 | export interface Result { 6 | text: null; 7 | result: null; 8 | } 9 | -------------------------------------------------------------------------------- /src/types/userstats/index.ts: -------------------------------------------------------------------------------- 1 | export interface UserstatResponse { 2 | groups: Group[]; 3 | statlistscollection: Userstat[]; 4 | } 5 | 6 | export interface Group { 7 | name: string; 8 | titleid: string; 9 | statlistscollection: GroupStatlistscollection[]; 10 | } 11 | 12 | export interface GroupStatlistscollection { 13 | arrangebyfield: string; 14 | arrangebyfieldid: string; 15 | stats: Userstat[]; 16 | } 17 | 18 | 19 | 20 | export interface Userstat { 21 | groupproperties: Groupproperties; 22 | xuid: string; 23 | scid: string; 24 | titleid?: string; 25 | name: string; 26 | type: string; 27 | value: string; 28 | properties: Properties; 29 | } 30 | 31 | export interface Groupproperties { 32 | Ordinal?: string; 33 | SortOrder?: string; 34 | DisplayName?: string; 35 | DisplayFormat?: string; 36 | DisplaySemantic?: string; 37 | } 38 | 39 | export interface Properties { 40 | DisplayName?: string; 41 | } -------------------------------------------------------------------------------- /src/types/xnotify/index.ts: -------------------------------------------------------------------------------- 1 | export interface StatusResponse { 2 | Status: StatusResponseStatus; 3 | CoreServices: CoreService[]; 4 | Titles: CoreService[]; 5 | } 6 | 7 | export interface CoreService { 8 | Id: number; 9 | Name: string; 10 | Status: Status; 11 | Scenarios: Scenario[]; 12 | } 13 | 14 | export interface Scenario { 15 | Id: number; 16 | Status: Status; 17 | Name: string; 18 | Devices: Status[]; 19 | Incidents: any[]; 20 | Description: string; 21 | } 22 | 23 | export interface Status { 24 | Id: number; 25 | Name: State; 26 | } 27 | 28 | export enum State { 29 | AndroidDevices = "Android devices", 30 | AppleDevices = "Apple devices", 31 | BackwardCompatibility = "Backward compatibility", 32 | CloudGaming = "Cloud gaming", 33 | None = "None", 34 | SmartTV = "Smart TV", 35 | WebServices = "Web services", 36 | Xbox360 = "Xbox 360", 37 | XboxOnWindows = "Xbox on Windows", 38 | XboxOne = "Xbox One", 39 | XboxOneS = "Xbox One S", 40 | XboxOneX = "Xbox One X", 41 | XboxSeriesS = "Xbox Series S", 42 | XboxSeriesX = "Xbox Series X", 43 | } 44 | 45 | export interface StatusResponseStatus { 46 | Overall: Overall; 47 | SelectedScenarios: Overall; 48 | } 49 | 50 | export interface Overall { 51 | State: State; 52 | Id: number; 53 | LastUpdated: Date; 54 | } 55 | -------------------------------------------------------------------------------- /tests/lib.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import XboxWebApi from '../src/lib' 3 | 4 | describe('XboxWebApi', function () { 5 | describe('#constructor()', function () { 6 | it('should be able to construct without error', function (done) { 7 | const client = new XboxWebApi({ 8 | uhs: 'uhs', 9 | token: '', 10 | }) 11 | 12 | assert.equal(client.constructor.name, 'XboxWebApi') 13 | 14 | done(); 15 | }); 16 | }); 17 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2021", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2015", 7 | "es2016", 8 | "es2017", 9 | "es2018", 10 | "DOM" 11 | ], 12 | "declaration": true, 13 | "declarationMap": true, 14 | "sourceMap": true, 15 | "outDir": "./dist", 16 | "rootDir": "./src/", 17 | "strict": true, 18 | "esModuleInterop": true, 19 | "noImplicitAny": false, 20 | "resolveJsonModule": true 21 | }, 22 | "include": [ 23 | "src/" 24 | ], 25 | "exclude": [ 26 | "**/*.spec.ts" 27 | ] 28 | } --------------------------------------------------------------------------------