├── .gitignore ├── .github ├── pull_request_template.md ├── issue_template.md ├── workflows │ └── ci.yml └── CONTRIBUTING.md ├── src ├── index.js ├── request │ ├── webapi-error.js │ ├── webapi-request.js │ ├── http-manager.js │ └── base-request.js ├── endpoints │ ├── streamers.js │ ├── leaderboards.js │ ├── puzzles.js │ ├── clubs.js │ ├── countries.js │ ├── tournaments.js │ ├── team-matches.js │ ├── player-data.js │ └── games.js ├── utils │ └── sort-parameters.js ├── queue │ └── index.js └── chess-web-api.js ├── documentation ├── logo.png ├── GAME.md └── logo.svg ├── .npmignore ├── .eslintrc.json ├── tests ├── streamers.test.js ├── leaderboards.test.js ├── if-changed.test.js ├── puzzles.test.js ├── countries.test.js ├── queue.test.js ├── tournaments.test.js ├── sort-parameters.test.js ├── team-matches.test.js ├── clubs.test.js ├── player-data.test.js └── games.test.js ├── LICENSE ├── package.json ├── README.md └── types └── main.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | ## Other Information 4 | 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const ChessWebAPI = require('./chess-web-api'); 2 | 3 | module.exports = ChessWebAPI; 4 | -------------------------------------------------------------------------------- /documentation/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyruwruw/chess-web-api/HEAD/documentation/logo.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | .eslintrc.json 4 | 5 | .github 6 | 7 | documentation 8 | 9 | tests 10 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | #### chess-web-api version: 2 | 3 | ## Steps to reproduce 4 | 5 | ## Expected behavior 6 | 7 | ## Actual behavior 8 | -------------------------------------------------------------------------------- /src/request/webapi-error.js: -------------------------------------------------------------------------------- 1 | function WebapiError(message, statusCode) { 2 | this.name = 'WebapiError'; 3 | this.message = message || ''; 4 | this.statusCode = statusCode; 5 | } 6 | 7 | WebapiError.prototype = Error.prototype; 8 | 9 | module.exports = WebapiError; 10 | -------------------------------------------------------------------------------- /src/request/webapi-request.js: -------------------------------------------------------------------------------- 1 | const Request = require('./base-request'); 2 | 3 | const DEFAULT_HOST = 'api.chess.com'; 4 | const DEFAULT_PORT = 443; 5 | const DEFAULT_SCHEME = 'https'; 6 | 7 | module.exports.builder = function () { 8 | return Request.builder() 9 | .withHost(DEFAULT_HOST) 10 | .withPort(DEFAULT_PORT) 11 | .withScheme(DEFAULT_SCHEME); 12 | }; 13 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "airbnb-base" 9 | ], 10 | "globals": { 11 | "Atomics": "readonly", 12 | "SharedArrayBuffer": "readonly" 13 | }, 14 | "parserOptions": { 15 | "ecmaVersion": 2018 16 | }, 17 | "rules": { 18 | "no-underscore-dangle": "off", 19 | "func-names": "off", 20 | "no-return-assign": "off", 21 | "no-console": "off" 22 | } 23 | } -------------------------------------------------------------------------------- /tests/streamers.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { 3 | getStreamers, 4 | } = require('../src/endpoints/streamers'); 5 | 6 | describe('Endpoints: Streamers', () => { 7 | describe('getStreamers', () => { 8 | it('Valid Request', async () => { 9 | try { 10 | expect.assertions(3); 11 | const data = await getStreamers(); 12 | 13 | expect(data.statusCode).toEqual(200); 14 | expect(data.body).toHaveProperty('streamers'); 15 | expect(data.body.streamers).toBeInstanceOf(Array); 16 | } catch (error) { 17 | console.log(error); 18 | } 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/endpoints/streamers.js: -------------------------------------------------------------------------------- 1 | const WebApiRequest = require('../request/webapi-request.js'); 2 | const HttpManager = require('../request/http-manager.js'); 3 | const { sortParameters } = require('../utils/sort-parameters'); 4 | 5 | function getStreamers(options, callback, headers) { 6 | const [_options, _callback, _headers] = sortParameters( 7 | ['object', 'function', 'object'], 8 | [options, callback, headers], 9 | ); 10 | 11 | return WebApiRequest.builder() 12 | .withPath('/pub/streamers') 13 | .withQueryParameters(_options) 14 | .withHeaders(_headers) 15 | .build() 16 | .execute(HttpManager.get, _callback); 17 | } 18 | 19 | module.exports = { 20 | getStreamers, 21 | }; 22 | -------------------------------------------------------------------------------- /src/endpoints/leaderboards.js: -------------------------------------------------------------------------------- 1 | const WebApiRequest = require('../request/webapi-request.js'); 2 | const HttpManager = require('../request/http-manager.js'); 3 | const { sortParameters } = require('../utils/sort-parameters'); 4 | 5 | function getLeaderboards(options, callback, headers) { 6 | const [_options, _callback, _headers] = sortParameters( 7 | ['object', 'function', 'object'], 8 | [options, callback, headers], 9 | ); 10 | 11 | return WebApiRequest.builder() 12 | .withPath('/pub/leaderboards') 13 | .withQueryParameters(_options) 14 | .withHeaders(_headers) 15 | .build() 16 | .execute(HttpManager.get, _callback); 17 | } 18 | 19 | module.exports = { 20 | getLeaderboards, 21 | }; 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | node_version: '14.x' 9 | 10 | jobs: 11 | test: 12 | name: Test 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: ${{ env.node_version }} 19 | - run: npm ci --prefer-offline 20 | - run: npm run test:ci 21 | 22 | lint: 23 | name: Lint 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | - uses: actions/setup-node@v1 28 | with: 29 | node-version: ${{ env.node_version }} 30 | - run: npm ci --prefer-offline 31 | - run: npm run lint 32 | -------------------------------------------------------------------------------- /tests/leaderboards.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { 3 | getLeaderboards, 4 | } = require('../src/endpoints/leaderboards'); 5 | 6 | describe('Endpoints: Leaderboards', () => { 7 | /** 8 | * Leaderboards test is unstable, possibly a problem with the API 9 | * Works most of the time, but occassionally gives 404 10 | * Give time and rerun test 11 | */ 12 | describe('getLeaderboards', () => { 13 | it('Valid Request', async () => { 14 | try { 15 | expect.assertions(3); 16 | const data = await getLeaderboards(); 17 | 18 | expect(data.statusCode).toEqual(200); 19 | expect(data.body).toHaveProperty('daily'); 20 | expect(data.body.daily).toBeInstanceOf(Array); 21 | } catch (error) { 22 | console.log(error); 23 | } 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Chess-Web-API 2 | 3 | ## Did you find a bug? 4 | 5 | - [Open a new issue](https://github.com/andyruwruw/chess-web-api/issues/new/choose) if it's not already reported under [issues](https://github.com/andyruwruw/chess-web-api/issues). 6 | 7 | - Be sure to add a clear title and description, giving as much information as possible to reproduce your issue. 8 | 9 | ## Did you write a patch that fixes a bug? 10 | 11 | - Open a new Github pull request 12 | 13 | - Ensure the PR description clearly describes the problem and solution. 14 | 15 | - Ensure all tests with jest run successfully (`npm run test`) or add description on tests changed/added. 16 | 17 | - Bump the package.json up a version! Example: 1.0.11 => 1.0.12 18 | 19 | - If I've disappeared and don't review, email me at [andrew@youngshome.com](andrew@youngshome.com) 20 | 21 | ## Want to help maintain the repo? 22 | 23 | - Send me an email! 24 | -------------------------------------------------------------------------------- /tests/if-changed.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const ChessWebAPI = require('../src/index'); 3 | 4 | const URL_ID = 'chess-com-developer-community'; 5 | 6 | let chessAPI = null; 7 | 8 | beforeAll(() => { 9 | chessAPI = new ChessWebAPI(); 10 | }); 11 | 12 | describe('Functionality: If Changed', () => { 13 | describe('ifChanged', () => { 14 | it('Unchanged', async () => { 15 | try { 16 | expect.assertions(2); 17 | const data = await chessAPI.getClub(URL_ID); 18 | const eTag = data.headers.etag; 19 | 20 | const result = await chessAPI.ifChanged( 21 | eTag, 22 | chessAPI.getClub, 23 | [URL_ID], 24 | ); 25 | 26 | expect(result).toHaveProperty('changed'); 27 | expect(result).toHaveProperty('response'); 28 | } catch (error) { 29 | console.log(error); 30 | } 31 | }); 32 | 33 | // If theres some value that is constantly changing? 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andrew Young 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. 22 | -------------------------------------------------------------------------------- /src/endpoints/puzzles.js: -------------------------------------------------------------------------------- 1 | const WebApiRequest = require('../request/webapi-request.js'); 2 | const HttpManager = require('../request/http-manager.js'); 3 | const { sortParameters } = require('../utils/sort-parameters'); 4 | 5 | function getDailyPuzzle(options, callback, headers) { 6 | const [_options, _callback, _headers] = sortParameters( 7 | ['object', 'function', 'object'], 8 | [options, callback, headers], 9 | ); 10 | 11 | return WebApiRequest.builder() 12 | .withPath('/pub/puzzle') 13 | .withQueryParameters(_options) 14 | .withHeaders(_headers) 15 | .build() 16 | .execute(HttpManager.get, _callback); 17 | } 18 | 19 | function getDailyPuzzleRandom(options, callback, headers) { 20 | const [_options, _callback, _headers] = sortParameters( 21 | ['object', 'function', 'object'], 22 | [options, callback, headers], 23 | ); 24 | 25 | return WebApiRequest.builder() 26 | .withPath('/pub/puzzle/random') 27 | .withQueryParameters(_options) 28 | .withHeaders(_headers) 29 | .build() 30 | .execute(HttpManager.get, _callback); 31 | } 32 | 33 | module.exports = { 34 | getDailyPuzzle, 35 | getDailyPuzzleRandom, 36 | }; 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chess-web-api", 3 | "version": "1.2.0", 4 | "description": "chess.com api wrapper", 5 | "main": "src/index.js", 6 | "types": "./types/main.d.ts", 7 | "scripts": { 8 | "lint": "eslint . --ignore-pattern node_modules/", 9 | "lint:fix": "eslint . --ignore-pattern node_modules/ --fix", 10 | "test": "jest --runInBand --detectOpenHandles", 11 | "test:ci": "jest --verbose --runInBand --detectOpenHandles", 12 | "test:watch": "jest --watch --runInBand --detectOpenHandles" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/andyruwruw/chess-web-api.git" 17 | }, 18 | "keywords": [ 19 | "Chess", 20 | "API", 21 | "chess.com", 22 | "wrapper" 23 | ], 24 | "author": "Andrew Young", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/andyruwruw/chess-web-api/issues" 28 | }, 29 | "homepage": "https://github.com/andyruwruw/chess-web-api#readme", 30 | "dependencies": { 31 | "chess.js": "^0.11.0", 32 | "superagent": "^5.2.1" 33 | }, 34 | "devDependencies": { 35 | "eslint": "^7.9.0", 36 | "eslint-config-airbnb-base": "^14.2.0", 37 | "eslint-plugin-import": "^2.22.0", 38 | "jest": "^26.4.2", 39 | "jest-each": "^26.6.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/puzzles.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { 3 | getDailyPuzzle, 4 | getDailyPuzzleRandom, 5 | } = require('../src/endpoints/puzzles'); 6 | 7 | describe('Endpoints: Puzzles', () => { 8 | describe('getDailyPuzzle', () => { 9 | it('Valid Request', async () => { 10 | try { 11 | expect.assertions(5); 12 | const data = await getDailyPuzzle(); 13 | 14 | expect(data.statusCode).toEqual(200); 15 | expect(data.body).toHaveProperty('title'); 16 | expect(data.body).toHaveProperty('url'); 17 | expect(data.body).toHaveProperty('fen'); 18 | expect(data.body).toHaveProperty('pgn'); 19 | } catch (error) { 20 | console.log(error); 21 | } 22 | }); 23 | }); 24 | 25 | describe('getDailyPuzzleRandom', () => { 26 | it('Valid Request', async () => { 27 | try { 28 | expect.assertions(5); 29 | const data = await getDailyPuzzleRandom(); 30 | 31 | expect(data.statusCode).toEqual(200); 32 | expect(data.body).toHaveProperty('title'); 33 | expect(data.body).toHaveProperty('url'); 34 | expect(data.body).toHaveProperty('fen'); 35 | expect(data.body).toHaveProperty('pgn'); 36 | } catch (error) { 37 | console.log(error); 38 | } 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/endpoints/clubs.js: -------------------------------------------------------------------------------- 1 | const WebApiRequest = require('../request/webapi-request.js'); 2 | const HttpManager = require('../request/http-manager.js'); 3 | const { sortParameters } = require('../utils/sort-parameters'); 4 | 5 | function getClub(urlID, options, callback, headers) { 6 | const [_options, _callback, _headers] = sortParameters( 7 | ['object', 'function', 'object'], 8 | [options, callback, headers], 9 | ); 10 | 11 | return WebApiRequest.builder() 12 | .withPath(`/pub/club/${urlID}`) 13 | .withQueryParameters(_options) 14 | .withHeaders(_headers) 15 | .build() 16 | .execute(HttpManager.get, _callback); 17 | } 18 | 19 | function getClubMembers(urlID, options, callback, headers) { 20 | const [_options, _callback, _headers] = sortParameters( 21 | ['object', 'function', 'object'], 22 | [options, callback, headers], 23 | ); 24 | 25 | return WebApiRequest.builder() 26 | .withPath(`/pub/club/${urlID}/members`) 27 | .withQueryParameters(_options) 28 | .withHeaders(_headers) 29 | .build() 30 | .execute(HttpManager.get, _callback); 31 | } 32 | 33 | function getClubMatches(urlID, options, callback, headers) { 34 | const [_options, _callback, _headers] = sortParameters( 35 | ['object', 'function', 'object'], 36 | [options, callback, headers], 37 | ); 38 | 39 | return WebApiRequest.builder() 40 | .withPath(`/pub/club/${urlID}/matches`) 41 | .withQueryParameters(_options) 42 | .withHeaders(_headers) 43 | .build() 44 | .execute(HttpManager.get, _callback); 45 | } 46 | 47 | module.exports = { 48 | getClub, 49 | getClubMembers, 50 | getClubMatches, 51 | }; 52 | -------------------------------------------------------------------------------- /src/endpoints/countries.js: -------------------------------------------------------------------------------- 1 | const WebApiRequest = require('../request/webapi-request.js'); 2 | const HttpManager = require('../request/http-manager.js'); 3 | const { sortParameters } = require('../utils/sort-parameters'); 4 | 5 | function getCountry(iso, options, callback, headers) { 6 | const [_options, _callback, _headers] = sortParameters( 7 | ['object', 'function', 'object'], 8 | [options, callback, headers], 9 | ); 10 | 11 | return WebApiRequest.builder() 12 | .withPath(`/pub/country/${iso}`) 13 | .withQueryParameters(_options) 14 | .withHeaders(_headers) 15 | .build() 16 | .execute(HttpManager.get, _callback); 17 | } 18 | 19 | function getCountryPlayers(iso, options, callback, headers) { 20 | const [_options, _callback, _headers] = sortParameters( 21 | ['object', 'function', 'object'], 22 | [options, callback, headers], 23 | ); 24 | 25 | return WebApiRequest.builder() 26 | .withPath(`/pub/country/${iso}/players`) 27 | .withQueryParameters(_options) 28 | .withHeaders(_headers) 29 | .build() 30 | .execute(HttpManager.get, _callback); 31 | } 32 | 33 | function getCountryClubs(iso, options, callback, headers) { 34 | const [_options, _callback, _headers] = sortParameters( 35 | ['object', 'function', 'object'], 36 | [options, callback, headers], 37 | ); 38 | 39 | return WebApiRequest.builder() 40 | .withPath(`/pub/country/${iso}/clubs`) 41 | .withQueryParameters(_options) 42 | .withHeaders(_headers) 43 | .build() 44 | .execute(HttpManager.get, _callback); 45 | } 46 | 47 | module.exports = { 48 | getCountry, 49 | getCountryPlayers, 50 | getCountryClubs, 51 | }; 52 | -------------------------------------------------------------------------------- /tests/countries.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { 3 | getCountry, 4 | getCountryPlayers, 5 | getCountryClubs, 6 | } = require('../src/endpoints/countries'); 7 | 8 | // US was failing due to 'getCountryPlayers' being such a huge file. 9 | const ISO = 'LU'; 10 | 11 | describe('Endpoints: Countries', () => { 12 | describe('getCountry', () => { 13 | it('Valid Request', async () => { 14 | try { 15 | expect.assertions(3); 16 | const data = await getCountry(ISO); 17 | 18 | expect(data.statusCode).toEqual(200); 19 | expect(data.body).toHaveProperty('code'); 20 | expect(data.body).toHaveProperty('name'); 21 | } catch (error) { 22 | console.log(error); 23 | } 24 | }); 25 | }); 26 | 27 | describe('getCountryPlayers', () => { 28 | it('Valid Request', async () => { 29 | try { 30 | expect.assertions(3); 31 | const data = await getCountryPlayers(ISO); 32 | 33 | expect(data.statusCode).toEqual(200); 34 | expect(data.body).toHaveProperty('players'); 35 | expect(data.body.players).toBeInstanceOf(Array); 36 | } catch (error) { 37 | console.log(error); 38 | } 39 | }); 40 | }); 41 | 42 | describe('getCountryClubs', () => { 43 | it('Valid Request', async () => { 44 | try { 45 | expect.assertions(3); 46 | const data = await getCountryClubs(ISO); 47 | 48 | expect(data.statusCode).toEqual(200); 49 | expect(data.body).toHaveProperty('clubs'); 50 | expect(data.body.clubs).toBeInstanceOf(Array); 51 | } catch (error) { 52 | console.log(error); 53 | } 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /src/utils/sort-parameters.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable valid-typeof */ 2 | /* eslint-disable no-console */ 3 | 4 | /** 5 | * Returns a default object. 6 | * 7 | * @param {string} type Type of object 8 | * @returns {*} Default object of that type 9 | */ 10 | function defaultObject(type) { 11 | if (type === 'string') { 12 | return ''; 13 | } if (type === 'object') { 14 | return {}; 15 | } if (type === 'function') { 16 | const noop = () => (null); 17 | return noop; 18 | } if (type === 'array') { 19 | return []; 20 | } 21 | return null; 22 | } 23 | 24 | /** 25 | * Defaults parameters if not present. 26 | * 27 | * @param {Array} types Array of each parameter's type 28 | * @param {Array<*>} parameters Arrat of parameters 29 | * @returns {Array<*>} Array of parameters with undefined values filled. 30 | */ 31 | function sortParameters(types, parameters) { 32 | const result = []; 33 | 34 | // Run through each parameter given 35 | for (let i = 0; i < parameters.length; i += 1) { 36 | if (parameters[i] !== undefined) { 37 | // Fill in unprovided parameters 38 | while ((result.length < types.length) 39 | && ((typeof (parameters[i]) !== types[result.length]) 40 | || parameters[i] instanceof Array) 41 | && (!(parameters[i] instanceof Array) 42 | || (parameters[i] instanceof Array 43 | && types[result.length] !== 'array'))) { 44 | result.push(defaultObject(types[result.length])); 45 | } 46 | if (result.length < types.length) { 47 | result.push(parameters[i]); 48 | } 49 | } 50 | } 51 | 52 | return result; 53 | } 54 | 55 | module.exports = { 56 | sortParameters, 57 | }; 58 | -------------------------------------------------------------------------------- /src/endpoints/tournaments.js: -------------------------------------------------------------------------------- 1 | const WebApiRequest = require('../request/webapi-request.js'); 2 | const HttpManager = require('../request/http-manager.js'); 3 | const { sortParameters } = require('../utils/sort-parameters'); 4 | 5 | function getTournament(urlID, options, callback, headers) { 6 | const [_options, _callback, _headers] = sortParameters( 7 | ['object', 'function', 'object'], 8 | [options, callback, headers], 9 | ); 10 | 11 | return WebApiRequest.builder() 12 | .withPath(`/pub/tournament/${urlID}`) 13 | .withQueryParameters(_options) 14 | .withHeaders(_headers) 15 | .build() 16 | .execute(HttpManager.get, _callback); 17 | } 18 | 19 | function getTournamentRound(urlID, round, options, callback, headers) { 20 | const [_options, _callback, _headers] = sortParameters( 21 | ['object', 'function', 'object'], 22 | [options, callback, headers], 23 | ); 24 | 25 | return WebApiRequest.builder() 26 | .withPath(`/pub/tournament/${urlID}/${round}`) 27 | .withQueryParameters(_options) 28 | .withHeaders(_headers) 29 | .build() 30 | .execute(HttpManager.get, _callback); 31 | } 32 | 33 | function getTournamentRoundGroup(urlID, round, group, options, callback, headers) { 34 | const [_options, _callback, _headers] = sortParameters( 35 | ['object', 'function', 'object'], 36 | [options, callback, headers], 37 | ); 38 | 39 | return WebApiRequest.builder() 40 | .withPath(`/pub/tournament/${urlID}/${round}/${group}`) 41 | .withQueryParameters(_options) 42 | .withHeaders(_headers) 43 | .build() 44 | .execute(HttpManager.get, _callback); 45 | } 46 | 47 | module.exports = { 48 | getTournament, 49 | getTournamentRound, 50 | getTournamentRoundGroup, 51 | }; 52 | -------------------------------------------------------------------------------- /tests/queue.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const Queue = require('../src/queue/index'); 3 | 4 | function QueueObject() { 5 | this.addMethods(Queue); 6 | this.queueSetup(); 7 | } 8 | 9 | QueueObject.prototype = { 10 | addMethods(methods) { 11 | const queueMethods = Object.keys(methods); 12 | 13 | for (let i = 0; i < queueMethods.length; i += 1) { 14 | this[queueMethods[i]] = methods[queueMethods[i]]; 15 | } 16 | }, 17 | 18 | add(a, b) { 19 | return a + b; 20 | }, 21 | }; 22 | 23 | let queue = null; 24 | 25 | describe('Functionality: Priority Queue', () => { 26 | beforeEach(() => { 27 | queue = new QueueObject(); 28 | }); 29 | 30 | afterEach(() => { 31 | queue = null; 32 | }); 33 | 34 | describe('Queue Setup', () => { 35 | it('Valid Instantiation', () => { 36 | try { 37 | expect.assertions(3); 38 | 39 | expect(queue).toHaveProperty('_running'); 40 | expect(queue).toHaveProperty('_requests'); 41 | expect(queue._requests).toBeInstanceOf(Array); 42 | } catch (error) { 43 | console.log(error); 44 | } 45 | }); 46 | }); 47 | 48 | describe('Dispatch', () => { 49 | it('Valid Call', async () => { 50 | try { 51 | expect.assertions(1); 52 | const method = jest.fn(() => Promise.resolve()); 53 | await queue.dispatch(method, () => (null), []); 54 | expect(method).toHaveBeenCalled(); 55 | } catch (error) { 56 | console.log(error); 57 | } 58 | }); 59 | }); 60 | 61 | describe('Dispatch with Parameters', () => { 62 | it('Valid Call', async () => { 63 | try { 64 | expect.assertions(1); 65 | const method = jest.fn(() => Promise.resolve()); 66 | await queue.dispatch(method, () => (null), ['1', '2']); 67 | expect(method).toHaveBeenLastCalledWith('1', '2', undefined); 68 | } catch (error) { 69 | console.log(error); 70 | } 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /src/endpoints/team-matches.js: -------------------------------------------------------------------------------- 1 | const WebApiRequest = require('../request/webapi-request.js'); 2 | const HttpManager = require('../request/http-manager.js'); 3 | const { sortParameters } = require('../utils/sort-parameters'); 4 | 5 | function getTeamMatch(id, options, callback, headers) { 6 | const [_options, _callback, _headers] = sortParameters( 7 | ['object', 'function', 'object'], 8 | [options, callback, headers], 9 | ); 10 | 11 | return WebApiRequest.builder() 12 | .withPath(`/pub/match/${id}`) 13 | .withQueryParameters(_options) 14 | .withHeaders(_headers) 15 | .build() 16 | .execute(HttpManager.get, _callback); 17 | } 18 | 19 | function getTeamMatchBoard(id, board, options, callback, headers) { 20 | const [_options, _callback, _headers] = sortParameters( 21 | ['object', 'function', 'object'], 22 | [options, callback, headers], 23 | ); 24 | 25 | return WebApiRequest.builder() 26 | .withPath(`/pub/match/${id}/${board}`) 27 | .withQueryParameters(_options) 28 | .withHeaders(_headers) 29 | .build() 30 | .execute(HttpManager.get, _callback); 31 | } 32 | 33 | function getTeamLiveMatch(id, options, callback, headers) { 34 | const [_options, _callback, _headers] = sortParameters( 35 | ['object', 'function', 'object'], 36 | [options, callback, headers], 37 | ); 38 | 39 | return WebApiRequest.builder() 40 | .withPath(`/pub/match/live/${id}`) 41 | .withQueryParameters(_options) 42 | .withHeaders(_headers) 43 | .build() 44 | .execute(HttpManager.get, _callback); 45 | } 46 | 47 | function getTeamLiveMatchBoard(id, board, options, callback, headers) { 48 | const [_options, _callback, _headers] = sortParameters( 49 | ['object', 'function', 'object'], 50 | [options, callback, headers], 51 | ); 52 | 53 | return WebApiRequest.builder() 54 | .withPath(`/pub/match/live/${id}/${board}`) 55 | .withQueryParameters(_options) 56 | .withHeaders(_headers) 57 | .build() 58 | .execute(HttpManager.get, _callback); 59 | } 60 | 61 | module.exports = { 62 | getTeamMatch, 63 | getTeamMatchBoard, 64 | getTeamLiveMatch, 65 | getTeamLiveMatchBoard, 66 | }; 67 | -------------------------------------------------------------------------------- /tests/tournaments.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { 3 | getTournament, 4 | getTournamentRound, 5 | getTournamentRoundGroup, 6 | } = require('../src/endpoints/tournaments'); 7 | 8 | const URL_ID = '-33rd-chesscom-quick-knockouts-1401-1600'; 9 | const ROUND = '1'; 10 | const GROUP = '1'; 11 | 12 | describe('Endpoints: Tournaments', () => { 13 | describe('getTournament', () => { 14 | it('Valid Request', async () => { 15 | try { 16 | expect.assertions(6); 17 | const data = await getTournament(URL_ID); 18 | 19 | expect(data.statusCode).toEqual(200); 20 | expect(data.body).toHaveProperty('name'); 21 | expect(data.body).toHaveProperty('url'); 22 | expect(data.body).toHaveProperty('status'); 23 | expect(data.body).toHaveProperty('players'); 24 | expect(data.body.players).toBeInstanceOf(Array); 25 | } catch (error) { 26 | console.log(error); 27 | } 28 | }); 29 | }); 30 | 31 | describe('getTournament', () => { 32 | it('Valid Request', async () => { 33 | try { 34 | expect.assertions(5); 35 | const data = await getTournamentRound(URL_ID, ROUND); 36 | 37 | expect(data.statusCode).toEqual(200); 38 | expect(data.body).toHaveProperty('groups'); 39 | expect(data.body.groups).toBeInstanceOf(Array); 40 | expect(data.body).toHaveProperty('players'); 41 | expect(data.body.players).toBeInstanceOf(Array); 42 | } catch (error) { 43 | console.log(error); 44 | } 45 | }); 46 | }); 47 | 48 | describe('getTournament', () => { 49 | it('Valid Request', async () => { 50 | try { 51 | expect.assertions(6); 52 | const data = await getTournamentRoundGroup(URL_ID, ROUND, GROUP); 53 | 54 | expect(data.statusCode).toEqual(200); 55 | expect(data.body).toHaveProperty('fair_play_removals'); 56 | expect(data.body).toHaveProperty('games'); 57 | expect(data.body.games).toBeInstanceOf(Array); 58 | expect(data.body).toHaveProperty('players'); 59 | expect(data.body.players).toBeInstanceOf(Array); 60 | } catch (error) { 61 | console.log(error); 62 | } 63 | }); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /tests/sort-parameters.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { sortParameters } = require('../src/utils/sort-parameters'); 3 | 4 | describe('Functionality: Sort Parameters', () => { 5 | describe('sortParameters', () => { 6 | it('Parameters Filled', () => { 7 | try { 8 | expect.assertions(6); 9 | const a = { type: 'Object' }; 10 | const b = () => ('Function 1'); 11 | const c = 'string'; 12 | const d = { type: 'Second Object' }; 13 | 14 | const [_a, _b, _c, _d] = sortParameters( 15 | ['object', 'function', 'string', 'object'], 16 | [a, b, c, d], 17 | ); 18 | 19 | expect(typeof (_a)).toBe('object'); 20 | expect(typeof (_b)).toBe('function'); 21 | expect(_b()).toBe('Function 1'); 22 | expect(typeof (_c)).toBe('string'); 23 | expect(typeof (_d)).toBe('object'); 24 | expect(d.type).toBe('Second Object'); 25 | } catch (error) { 26 | console.log(error); 27 | } 28 | }); 29 | 30 | it('Parameters Missing', () => { 31 | try { 32 | expect.assertions(5); 33 | const a = { type: 'Object' }; 34 | const b = undefined; 35 | const c = 'string'; 36 | const d = { type: 'Second Object' }; 37 | 38 | const [_a, _b, _c, _d] = sortParameters( 39 | ['object', 'function', 'string', 'object'], 40 | [a, b, c, d], 41 | ); 42 | 43 | expect(typeof (_a)).toBe('object'); 44 | expect(typeof (_b)).toBe('function'); 45 | expect(_b()).toBe(null); 46 | expect(typeof (_c)).toBe('string'); 47 | expect(typeof (_d)).toBe('object'); 48 | } catch (error) { 49 | console.log(error); 50 | } 51 | }); 52 | 53 | it('Parameter Last', () => { 54 | try { 55 | expect.assertions(5); 56 | const a = undefined; 57 | const b = undefined; 58 | const c = undefined; 59 | const d = []; 60 | 61 | const [_a, _b, _c, _d] = sortParameters( 62 | ['object', 'function', 'string', 'array'], 63 | [a, b, c, d], 64 | ); 65 | 66 | expect(typeof (_a)).toBe('object'); 67 | expect(typeof (_b)).toBe('function'); 68 | expect(_b()).toBe(null); 69 | expect(typeof (_c)).toBe('string'); 70 | expect(_d).toBeInstanceOf(Array); 71 | } catch (error) { 72 | console.log(error); 73 | } 74 | }); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /tests/team-matches.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { 3 | getTeamMatch, 4 | getTeamMatchBoard, 5 | getTeamLiveMatch, 6 | getTeamLiveMatchBoard, 7 | } = require('../src/endpoints/team-matches'); 8 | 9 | const ID = '12803'; 10 | const BOARD = '3'; 11 | const LIVE_ID = '5833'; 12 | 13 | describe('Endpoints: Team Matches', () => { 14 | describe('getTeamMatch', () => { 15 | it('Valid Request', async () => { 16 | try { 17 | expect.assertions(5); 18 | const data = await getTeamMatch(ID); 19 | 20 | expect(data.statusCode).toEqual(200); 21 | expect(data.body).toHaveProperty('name'); 22 | expect(data.body).toHaveProperty('url'); 23 | expect(data.body).toHaveProperty('status'); 24 | expect(data.body).toHaveProperty('teams'); 25 | } catch (error) { 26 | console.log(error); 27 | } 28 | }); 29 | }); 30 | 31 | describe('getTeamMatchBoard', () => { 32 | it('Valid Request', async () => { 33 | try { 34 | expect.assertions(4); 35 | const data = await getTeamMatchBoard(ID, BOARD); 36 | 37 | expect(data.statusCode).toEqual(200); 38 | expect(data.body).toHaveProperty('board_scores'); 39 | expect(data.body).toHaveProperty('games'); 40 | expect(data.body.games).toBeInstanceOf(Array); 41 | } catch (error) { 42 | console.log(error); 43 | } 44 | }); 45 | }); 46 | 47 | describe('getTeamLiveMatch', () => { 48 | it('Valid Request', async () => { 49 | try { 50 | expect.assertions(5); 51 | const data = await getTeamLiveMatch(LIVE_ID); 52 | 53 | expect(data.statusCode).toEqual(200); 54 | expect(data.body).toHaveProperty('name'); 55 | expect(data.body).toHaveProperty('url'); 56 | expect(data.body).toHaveProperty('status'); 57 | expect(data.body).toHaveProperty('teams'); 58 | } catch (error) { 59 | console.log(error); 60 | } 61 | }); 62 | }); 63 | 64 | describe('getTeamLiveMatchBoard', () => { 65 | it('Valid Request', async () => { 66 | try { 67 | expect.assertions(4); 68 | const data = await getTeamLiveMatchBoard(LIVE_ID, BOARD); 69 | 70 | expect(data.statusCode).toEqual(200); 71 | expect(data.body).toHaveProperty('board_scores'); 72 | expect(data.body).toHaveProperty('games'); 73 | expect(data.body.games).toBeInstanceOf(Array); 74 | } catch (error) { 75 | console.log(error); 76 | } 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /tests/clubs.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { 3 | getClub, 4 | getClubMembers, 5 | getClubMatches, 6 | } = require('../src/endpoints/clubs'); 7 | 8 | const URL_ID = 'chess-com-developer-community'; 9 | 10 | describe('Endpoints: Clubs', () => { 11 | describe('getClub', () => { 12 | it('Valid Request', async () => { 13 | try { 14 | expect.assertions(5); 15 | const data = await getClub(URL_ID); 16 | 17 | expect(data.statusCode).toEqual(200); 18 | 19 | expect(data.body).toHaveProperty('club_id'); 20 | expect(data.body).toHaveProperty('url'); 21 | expect(data.body).toHaveProperty('name'); 22 | expect(data.body).toHaveProperty('members_count'); 23 | } catch (error) { 24 | console.log(error); 25 | } 26 | }); 27 | 28 | it('Missing ID', async () => { 29 | try { 30 | expect.assertions(3); 31 | const data = await getClub(); 32 | 33 | expect(data.statusCode).toEqual(200); 34 | expect(data.body).toHaveProperty('name'); 35 | expect(data.body.name).toEqual('undefined'); 36 | } catch (error) { 37 | console.log(error); 38 | } 39 | }); 40 | }); 41 | 42 | describe('getClubMembers', () => { 43 | it('Valid Request', async () => { 44 | try { 45 | expect.assertions(3); 46 | const data = await getClubMembers(URL_ID); 47 | 48 | expect(data.statusCode).toEqual(200); 49 | expect(data.body).toHaveProperty('weekly'); 50 | expect(data.body.weekly).toBeInstanceOf(Array); 51 | } catch (error) { 52 | console.log(error); 53 | } 54 | }); 55 | 56 | it('Missing ID', async () => { 57 | try { 58 | expect.assertions(3); 59 | const data = await getClubMembers(); 60 | 61 | expect(data.statusCode).toEqual(200); 62 | expect(data.body).toHaveProperty('weekly'); 63 | expect(data.body.weekly).toEqual([]); 64 | } catch (error) { 65 | console.log(error); 66 | } 67 | }); 68 | }); 69 | 70 | describe('getClubMatches', () => { 71 | it('Valid Request', async () => { 72 | try { 73 | expect.assertions(5); 74 | const data = await getClubMatches(URL_ID); 75 | 76 | expect(data.statusCode).toEqual(200); 77 | expect(data.body).toHaveProperty('finished'); 78 | expect(data.body).toHaveProperty('in_progress'); 79 | expect(data.body).toHaveProperty('registered'); 80 | expect(data.body.finished).toBeInstanceOf(Array); 81 | } catch (error) { 82 | console.log(error); 83 | } 84 | }); 85 | 86 | it('Missing ID', async () => { 87 | try { 88 | expect.assertions(5); 89 | const data = await getClubMatches(); 90 | 91 | expect(data.statusCode).toEqual(200); 92 | expect(data.body).toHaveProperty('finished'); 93 | expect(data.body).toHaveProperty('in_progress'); 94 | expect(data.body).toHaveProperty('registered'); 95 | expect(data.body.finished).toEqual([]); 96 | } catch (error) { 97 | console.log(error); 98 | } 99 | }); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /src/queue/index.js: -------------------------------------------------------------------------------- 1 | const { sortParameters } = require('../utils/sort-parameters'); 2 | 3 | /** 4 | * Sets up priority queue 5 | */ 6 | function queueSetup() { 7 | this._requests = []; 8 | this._running = false; 9 | } 10 | 11 | /** 12 | * Prepares a request to be added to the queue 13 | * 14 | * @param {function} method chess-web-api function for request 15 | * @param {function} callback Function to be called with result 16 | * @param {array} parameters Array of parameter to be passed into chess-web-api function 17 | * @param {object} [options] Added query parameters to the end of the URL 18 | * @param {array} [callbackParameters] Array of parameters to be padded on to callback method. 19 | * @param {number} [priority] Prirority in queue (1 in highest) 20 | */ 21 | function dispatch(method, callback, parameters, options, callbackParameters, priority) { 22 | if (!method || typeof (method) !== 'function') { 23 | throw Error('dispatch requires request function'); 24 | } 25 | if (!callback || typeof (callback) !== 'function') { 26 | throw Error('dispatch requires callback function'); 27 | } 28 | 29 | const [_parameters, _options, _callbackParameters, _priority] = sortParameters( 30 | ['array', 'object', 'array', 'number'], 31 | [parameters, options, callbackParameters, priority], 32 | ); 33 | 34 | const request = { 35 | method, 36 | callback, 37 | parameters: _parameters, 38 | options: _options, 39 | priority: _priority || 1, 40 | callbackParameters: _callbackParameters || [], 41 | }; 42 | 43 | this.enqueue(request); 44 | } 45 | 46 | /** 47 | * Adds a request to the queue 48 | * @param {object} request Request object from dispatch 49 | */ 50 | function enqueue(request) { 51 | let contain = false; 52 | 53 | for (let i = 0; i < this._requests.length; i += 1) { 54 | if (this._requests[i].priority > request.priority) { 55 | this._requests.splice(i, 0, request); 56 | contain = true; 57 | break; 58 | } 59 | } 60 | if (!contain) { 61 | this._requests.push(request); 62 | } 63 | if (!this._running) { 64 | this.startRequests(); 65 | } 66 | } 67 | 68 | /** 69 | * Retrieves / removes first request in queue 70 | * @returns {object} Request object from dispatch 71 | */ 72 | function dequeue() { 73 | if (this._requests.length === 0) { 74 | return null; 75 | } 76 | return (this._requests.shift()); 77 | } 78 | 79 | /** 80 | * Removes all previous requests 81 | */ 82 | function clearQueue() { 83 | this._requests = []; 84 | } 85 | 86 | /** 87 | * Runs each request consecutively 88 | */ 89 | async function startRequests() { 90 | while (this._requests.length > 0) { 91 | if (!this._running) { 92 | this._running = true; 93 | } 94 | const request = this.dequeue(); 95 | const { method, callback } = request; 96 | let error = null; 97 | // eslint-disable-next-line no-await-in-loop 98 | const response = await method(...request.parameters, request.options).catch((e) => error = e); 99 | callback(response, error, ...request.callbackParameters); 100 | } 101 | this._running = false; 102 | } 103 | 104 | module.exports = { 105 | queueSetup, 106 | dispatch, 107 | enqueue, 108 | dequeue, 109 | clearQueue, 110 | startRequests, 111 | }; 112 | -------------------------------------------------------------------------------- /src/chess-web-api.js: -------------------------------------------------------------------------------- 1 | const Queue = require('./queue/index'); 2 | const { 3 | getPlayer, 4 | getPlayerStats, 5 | getPlayerOnline, 6 | getPlayerCurrentDailyChess, 7 | getPlayerToMoveDailyChess, 8 | getPlayerMonthlyArchives, 9 | getPlayerCompleteMonthlyArchives, 10 | getPlayerMultiGamePGN, 11 | getPlayerClubs, 12 | getPlayerMatches, 13 | getPlayerTournaments, 14 | getTitledPlayers, 15 | } = require('./endpoints/player-data'); 16 | const { 17 | getClub, 18 | getClubMembers, 19 | getClubMatches, 20 | } = require('./endpoints/clubs'); 21 | const { 22 | getTournament, 23 | getTournamentRound, 24 | getTournamentRoundGroup, 25 | } = require('./endpoints/tournaments'); 26 | const { 27 | getTeamMatch, 28 | getTeamMatchBoard, 29 | getTeamLiveMatch, 30 | getTeamLiveMatchBoard, 31 | } = require('./endpoints/team-matches'); 32 | const { 33 | getCountry, 34 | getCountryPlayers, 35 | getCountryClubs, 36 | } = require('./endpoints/countries'); 37 | const { 38 | getDailyPuzzle, 39 | getDailyPuzzleRandom, 40 | } = require('./endpoints/puzzles'); 41 | const { 42 | getStreamers, 43 | } = require('./endpoints/streamers'); 44 | const { 45 | getLeaderboards, 46 | } = require('./endpoints/leaderboards'); 47 | const { 48 | getGameByID, 49 | } = require('./endpoints/games'); 50 | 51 | function ChessWebAPI(options) { 52 | if (options && 'queue' in options && options.queue) { 53 | this.addMethods(Queue); 54 | this.queueSetup(); 55 | } 56 | } 57 | 58 | ChessWebAPI.prototype = { 59 | addMethods(methods) { 60 | const queueMethods = Object.keys(methods); 61 | 62 | for (let i = 0; i < queueMethods.length; i += 1) { 63 | this[queueMethods[i]] = methods[queueMethods[i]]; 64 | } 65 | }, 66 | 67 | async ifChanged(etag, method, parameters, options, callback) { 68 | if (!etag || typeof etag !== 'string') { 69 | throw Error('etag required for ifChanged'); 70 | } 71 | if (!method || typeof method !== 'function') { 72 | throw Error('dispatch requires request function'); 73 | } 74 | 75 | let actualParameters = []; 76 | let actualCallback = null; 77 | let actualOptions = {}; 78 | 79 | const items = [parameters, options, callback]; 80 | 81 | for (let i = 0; i < items.length; i += 1) { 82 | if (items[i]) { 83 | if (items[i] instanceof Array) { 84 | actualParameters = items[i]; 85 | } else if (typeof items[i] === 'object') { 86 | actualOptions = items[i]; 87 | } else if (typeof items[i] === 'function') { 88 | actualCallback = items[i]; 89 | } 90 | } 91 | } 92 | 93 | try { 94 | const data = await method( 95 | ...actualParameters, 96 | actualOptions, 97 | actualCallback, 98 | { 99 | 'If-None-Match': etag, 100 | }, 101 | ); 102 | 103 | const response = { 104 | changed: true, 105 | response: data, 106 | }; 107 | 108 | return response; 109 | } catch (error) { 110 | return { changed: false }; 111 | } 112 | }, 113 | 114 | getPlayer, 115 | getPlayerStats, 116 | getPlayerOnline, 117 | getPlayerCurrentDailyChess, 118 | getPlayerToMoveDailyChess, 119 | getPlayerMonthlyArchives, 120 | getPlayerCompleteMonthlyArchives, 121 | getPlayerMultiGamePGN, 122 | getPlayerClubs, 123 | getPlayerMatches, 124 | getPlayerTournaments, 125 | getTitledPlayers, 126 | 127 | getClub, 128 | getClubMembers, 129 | getClubMatches, 130 | 131 | getTournament, 132 | getTournamentRound, 133 | getTournamentRoundGroup, 134 | 135 | getTeamMatch, 136 | getTeamMatchBoard, 137 | getTeamLiveMatch, 138 | getTeamLiveMatchBoard, 139 | 140 | getCountry, 141 | getCountryPlayers, 142 | getCountryClubs, 143 | 144 | getDailyPuzzle, 145 | getDailyPuzzleRandom, 146 | 147 | getStreamers, 148 | 149 | getLeaderboards, 150 | 151 | getGameByID, 152 | }; 153 | 154 | module.exports = ChessWebAPI; 155 | -------------------------------------------------------------------------------- /src/request/http-manager.js: -------------------------------------------------------------------------------- 1 | const superagent = require('superagent'); 2 | const WebApiError = require('./webapi-error'); 3 | 4 | const HttpManager = {}; 5 | 6 | /* Create superagent options from the base request */ 7 | const _getParametersFromRequest = function (request) { 8 | const options = {}; 9 | 10 | if (request.getQueryParameters()) { 11 | options.query = request.getQueryParameters(); 12 | } 13 | 14 | if (request.getHeaders() 15 | && request.getHeaders()['Content-Type'] === 'application/json') { 16 | options.data = JSON.stringify(request.getBodyParameters()); 17 | } else if (request.getBodyParameters()) { 18 | options.data = request.getBodyParameters(); 19 | } 20 | 21 | if (request.getHeaders()) { 22 | options.headers = request.getHeaders(); 23 | } 24 | return options; 25 | }; 26 | 27 | /* Create an error object from an error returned from the Web API */ 28 | const _getErrorObject = function (defaultMessage, err) { 29 | let errorObject; 30 | if (typeof err.error === 'object' && typeof err.error.message === 'string') { 31 | // Web API Error format 32 | errorObject = new WebApiError(err.error.message, err.error.status); 33 | } else if (typeof err.error === 'string') { 34 | // Authorization Error format 35 | errorObject = new WebApiError(`${err.error}: ${err.error_description}`); 36 | } else if (typeof err === 'string') { 37 | // Serialized JSON error 38 | try { 39 | const parsedError = JSON.parse(err); 40 | errorObject = new WebApiError( 41 | parsedError.error.message, 42 | parsedError.error.status, 43 | ); 44 | } catch (error) { 45 | // Error not JSON formatted 46 | } 47 | } 48 | 49 | if (!errorObject) { 50 | // Unexpected format 51 | errorObject = new WebApiError(`${defaultMessage}: ${JSON.stringify(err)}`); 52 | } 53 | 54 | return errorObject; 55 | }; 56 | 57 | /* Make the request to the Web API */ 58 | HttpManager._makeRequest = function (method, options, uri, callback) { 59 | const req = method.bind(superagent)(uri); 60 | 61 | if (options.query) { 62 | req.query(options.query); 63 | } 64 | 65 | if (options.data 66 | && (!options.headers || options.headers['Content-Type'] !== 'application/json')) { 67 | req.type('form'); 68 | req.send(options.data); 69 | } else if (options.data) { 70 | req.send(options.data); 71 | } 72 | 73 | if (options.headers) { 74 | req.set(options.headers); 75 | } 76 | 77 | req.end((err, response) => { 78 | if (err) { 79 | const errorObject = _getErrorObject('Request error', { 80 | error: err, 81 | }); 82 | return callback(errorObject); 83 | } 84 | 85 | return callback(null, { 86 | body: response.body, 87 | headers: response.headers, 88 | statusCode: response.statusCode, 89 | }); 90 | }); 91 | }; 92 | 93 | /** 94 | * Make a HTTP GET request. 95 | * @param {BaseRequest} The request. 96 | * @param {Function} The callback function. 97 | */ 98 | HttpManager.get = function (request, callback) { 99 | const options = _getParametersFromRequest(request); 100 | const method = superagent.get; 101 | 102 | HttpManager._makeRequest(method, options, request.getURI(), callback); 103 | }; 104 | 105 | /** 106 | * Make a HTTP POST request. 107 | * @param {BaseRequest} The request. 108 | * @param {Function} The callback function. 109 | */ 110 | HttpManager.post = function (request, callback) { 111 | const options = _getParametersFromRequest(request); 112 | const method = superagent.post; 113 | 114 | HttpManager._makeRequest(method, options, request.getURI(), callback); 115 | }; 116 | 117 | /** 118 | * Make a HTTP DELETE request. 119 | * @param {BaseRequest} The request. 120 | * @param {Function} The callback function. 121 | */ 122 | HttpManager.del = function (request, callback) { 123 | const options = _getParametersFromRequest(request); 124 | const method = superagent.del; 125 | 126 | HttpManager._makeRequest(method, options, request.getURI(), callback); 127 | }; 128 | 129 | /** 130 | * Make a HTTP PUT request. 131 | * @param {BaseRequest} The request. 132 | * @param {Function} The callback function. 133 | */ 134 | HttpManager.put = function (request, callback) { 135 | const options = _getParametersFromRequest(request); 136 | const method = superagent.put; 137 | 138 | HttpManager._makeRequest(method, options, request.getURI(), callback); 139 | }; 140 | 141 | module.exports = HttpManager; 142 | -------------------------------------------------------------------------------- /src/request/base-request.js: -------------------------------------------------------------------------------- 1 | const Request = function (builder) { 2 | if (!builder) { 3 | throw new Error('No builder supplied to constructor'); 4 | } 5 | 6 | this.host = builder.host; 7 | this.port = builder.port; 8 | this.scheme = builder.scheme; 9 | this.queryParameters = builder.queryParameters; 10 | this.bodyParameters = builder.bodyParameters; 11 | this.headers = builder.headers; 12 | this.path = builder.path; 13 | 14 | if (this.headers === undefined) { 15 | this.headers = { 'User-Agent': 'chess-web-api/1.1.3' }; 16 | } else if ((typeof this.headers === 'object') && !Object.prototype.hasOwnProperty.call(this.headers, 'User-Agent')) { 17 | this.headers['User-Agent'] = 'chess-web-api/1.1.3'; 18 | } 19 | }; 20 | 21 | Request.prototype._getter = function (key) { 22 | return function () { 23 | return this[key]; 24 | }; 25 | }; 26 | 27 | Request.prototype.getHost = Request.prototype._getter('host'); 28 | 29 | Request.prototype.getPort = Request.prototype._getter('port'); 30 | 31 | Request.prototype.getScheme = Request.prototype._getter('scheme'); 32 | 33 | Request.prototype.getPath = Request.prototype._getter('path'); 34 | 35 | Request.prototype.getQueryParameters = Request.prototype._getter( 36 | 'queryParameters', 37 | ); 38 | 39 | Request.prototype.getBodyParameters = Request.prototype._getter( 40 | 'bodyParameters', 41 | ); 42 | 43 | Request.prototype.getHeaders = Request.prototype._getter('headers'); 44 | 45 | Request.prototype.getURI = function () { 46 | if (!this.scheme || !this.host || !this.port) { 47 | throw new Error('Missing components necessary to construct URI'); 48 | } 49 | let uri = `${this.scheme}://${this.host}`; 50 | if ((this.scheme === 'http' && this.port !== 80) 51 | || (this.scheme === 'https' && this.port !== 443)) { 52 | uri += `:${this.port}`; 53 | } 54 | if (this.path) { 55 | uri += this.path; 56 | } 57 | return uri; 58 | }; 59 | 60 | Request.prototype.getURL = function () { 61 | const uri = this.getURI(); 62 | if (this.getQueryParameters()) { 63 | return uri + this.getQueryParameterString(this.getQueryParameters()); 64 | } 65 | return uri; 66 | }; 67 | 68 | Request.prototype.getQueryParameterString = function () { 69 | const queryParameters = this.getQueryParameters(); 70 | if (queryParameters) { 71 | return ( 72 | `?${ 73 | Object.keys(queryParameters) 74 | .filter((key) => queryParameters[key] !== undefined) 75 | .map((key) => `${key}=${queryParameters[key]}`) 76 | .join('&')}` 77 | ); 78 | } 79 | return ''; 80 | }; 81 | 82 | Request.prototype.execute = function (method, callback) { 83 | if (callback) { 84 | method(this, callback); 85 | return null; 86 | } 87 | const _self = this; 88 | 89 | return new Promise(((resolve, reject) => { 90 | method(_self, (error, result) => { 91 | if (error) { 92 | reject(error); 93 | } else { 94 | resolve(result); 95 | } 96 | }); 97 | })); 98 | }; 99 | 100 | const Builder = function () {}; 101 | 102 | Builder.prototype._setter = function (key) { 103 | return function (value) { 104 | this[key] = value; 105 | return this; 106 | }; 107 | }; 108 | 109 | Builder.prototype.withHost = Builder.prototype._setter('host'); 110 | 111 | Builder.prototype.withPort = Builder.prototype._setter('port'); 112 | 113 | Builder.prototype.withScheme = Builder.prototype._setter('scheme'); 114 | 115 | Builder.prototype.withPath = Builder.prototype._setter('path'); 116 | 117 | Builder.prototype._assigner = function (key) { 118 | return function () { 119 | for (let i = 0; i < arguments.length; i += 1) { 120 | // eslint-disable-next-line prefer-rest-params 121 | this[key] = this._assign(this[key], arguments[i]); 122 | } 123 | return this; 124 | }; 125 | }; 126 | 127 | Builder.prototype.withQueryParameters = Builder.prototype._assigner( 128 | 'queryParameters', 129 | ); 130 | 131 | Builder.prototype.withBodyParameters = Builder.prototype._assigner( 132 | 'bodyParameters', 133 | ); 134 | 135 | Builder.prototype.withHeaders = Builder.prototype._assigner('headers'); 136 | 137 | Builder.prototype._assign = function (src, obj) { 138 | if (obj && Array.isArray(obj)) { 139 | return obj; 140 | } 141 | if (obj && Object.keys(obj).length > 0) { 142 | return Object.assign(src || {}, obj); 143 | } 144 | return src; 145 | }; 146 | 147 | Builder.prototype.build = function () { 148 | return new Request(this); 149 | }; 150 | 151 | module.exports.builder = function () { 152 | return new Builder(); 153 | }; 154 | -------------------------------------------------------------------------------- /documentation/GAME.md: -------------------------------------------------------------------------------- 1 | # Get a Chess Game by ID 2 | 3 | #### Description: 4 | 5 | chess-web-api's `getGameById` method is not an official endpoint of [Chess.com's Published Data API](https://www.chess.com/news/view/published-data-api). It uses a callback from Chess.com's website to get its data. 6 | 7 | Therefore it is highly unstable and could be changed without warning or stop functioning. 8 | 9 | The endpoint was requested by pi0neerpat and jschiarizzi and it's implementation was discussed [here](https://github.com/andyruwruw/chess-web-api/issues/10) and [here](https://github.com/andyruwruw/chess-web-api/issues/11). It utilizes the endpoint the official website uses to show your game. 10 | 11 | A PGN was not provided, but a FEN and encrypted move list were. So `chess.js` was added as a dependency to generate the PGN. Thanks to CoeJoder for implementing it. 12 | 13 | #### Returns: 14 | 15 | ``` 16 | { 17 | "body": { 18 | "game": { 19 | "allowVacation": boolean, 20 | "baseTime1": number, 21 | "canSendTrophy": boolean, 22 | "changesPlayersRating": number, 23 | "colorOfWinner": string, 24 | "endTime": number, 25 | "id": number, 26 | "initialSetup": string, 27 | "isLiveGame": boolean, 28 | "isAbortable": boolean, 29 | "isAnalyzable": boolean, 30 | "isCheckmate": boolean, 31 | "isStalemate": boolean, 32 | "isFinished": boolean, 33 | "isRated": boolean, 34 | "isResignable": boolean, 35 | "lastMove": string, // Number 36 | "moveList": string, // See implementation github issue 37 | "moveTimestamps": string, // Comma separated numbers. Examples: '1800,1800,1782,1713,1740' 38 | "pgn": string, // Example: '[Event "Live Chess"]\n' + '[Site "Chess.com"]\n'... 39 | "pgnHeaders": { 40 | "Black": string, // Username 41 | "BlackElo": number, 42 | "Date": string, // YYY.MM.DD 43 | "ECO": string, 44 | "Event": "Let\'s Play!", 45 | "FEN": string, 46 | "Result": string, 47 | "SetUp": string, 48 | "Site": "Chess.com", 49 | "TimeControl": string, 50 | "White": string, 51 | "WhiteElo": string, 52 | }, 53 | "plyCount": number, 54 | "ratingChangeWhite": number, 55 | "ratingChangeBlack": number, 56 | "resultMessage": string, // Example: 'proanalyst won on time' 57 | "timeIncrement1": 0, 58 | "turnColor": string, 59 | "type": string, // Example: 'chess' 60 | "typeName": string, // Example: 'Standard Chess' 61 | }, 62 | "players": { 63 | "top": { 64 | "avatarUrl": string, // URL 65 | "canWinOnTime": boolean, 66 | "color": string, 67 | "countryId": number, 68 | "countryName": string, 69 | "defaultTab": number, 70 | "flairCode": string, 71 | "gamesInProgress": number, 72 | "hasMovedAtLeastOnce": boolean, 73 | "id": number, 74 | "isContentHidden": boolean, 75 | "isDrawable": boolean, 76 | "isEnabled": boolean, 77 | "isInLivechess": boolean, 78 | "isOnline": boolean, 79 | "isTouchMove": boolean, 80 | "isVacation": boolean, 81 | "isWhiteOnBottom": boolean, 82 | "lastLoginDate": number, 83 | "location": string, 84 | "memberSince": number, 85 | "membershipCode": string, 86 | "membershipLevel": number, 87 | "offeredDraw": boolean, 88 | "postMoveAction": string, 89 | "rating": number, 90 | "turnTimeRemaining": string, // Example '3 Days' 91 | "username": string, 92 | "vacationRemaining": string, // Example '9 days' 93 | }, 94 | "bottom": { 95 | "avatarUrl": string, // URL 96 | "canWinOnTime": boolean, 97 | "color": string, 98 | "countryId": number, 99 | "countryName": string, 100 | "defaultTab": number, 101 | "flairCode": string, 102 | "gamesInProgress": number, 103 | "hasMovedAtLeastOnce": boolean, 104 | "id": number, 105 | "isContentHidden": boolean, 106 | "isDrawable": boolean, 107 | "isEnabled": boolean, 108 | "isInLivechess": boolean, 109 | "isOnline": boolean, 110 | "isTouchMove": boolean, 111 | "isVacation": boolean, 112 | "isWhiteOnBottom": boolean, 113 | "lastLoginDate": number, 114 | "location": string, 115 | "memberSince": number, 116 | "membershipCode": string, 117 | "membershipLevel": number, 118 | "offeredDraw": boolean, 119 | "postMoveAction": string, 120 | "rating": number, 121 | "turnTimeRemaining": string, // Example '3 Days' 122 | "username": string, 123 | "vacationRemaining": string, // Example '9 days' 124 | }, 125 | } 126 | }, 127 | "headers": { 128 | "date": string, 129 | "set-cookie": [string], // There are some values here... 130 | "cache-control": "no-cache, private", 131 | "x-xss-protection": "1; mode=block", 132 | "x-chesscom-features": string, 133 | "x-chesscom-version": string, // Number 134 | "x-chesscom-matched": string, 135 | "x-chesscom-request-id-lb": string, 136 | "x-chesscom-request-id-cdn": string, 137 | }, 138 | "statusCode": 200 139 | } 140 | ``` 141 | -------------------------------------------------------------------------------- /src/endpoints/player-data.js: -------------------------------------------------------------------------------- 1 | const WebApiRequest = require('../request/webapi-request.js'); 2 | const HttpManager = require('../request/http-manager.js'); 3 | const { sortParameters } = require('../utils/sort-parameters'); 4 | 5 | function getPlayer(username, options, callback, headers) { 6 | const [_options, _callback, _headers] = sortParameters( 7 | ['object', 'function', 'object'], 8 | [options, callback, headers], 9 | ); 10 | 11 | return WebApiRequest.builder() 12 | .withPath(`/pub/player/${username}`) 13 | .withQueryParameters(_options) 14 | .withHeaders(_headers) 15 | .build() 16 | .execute(HttpManager.get, _callback); 17 | } 18 | 19 | function getPlayerStats(username, options, callback, headers) { 20 | const [_options, _callback, _headers] = sortParameters( 21 | ['object', 'function', 'object'], 22 | [options, callback, headers], 23 | ); 24 | 25 | return WebApiRequest.builder() 26 | .withPath(`/pub/player/${username}/stats`) 27 | .withQueryParameters(_options) 28 | .withHeaders(_headers) 29 | .build() 30 | .execute(HttpManager.get, _callback); 31 | } 32 | 33 | // eslint-disable-next-line no-unused-vars 34 | function getPlayerOnline(username, options, callback, headers) { 35 | return new Error('This endpoint was removed by Chess.com, please see https://github.com/andyruwruw/chess-web-api/tree/master#getplayeronlineusername-options-callback'); 36 | } 37 | 38 | function getPlayerCurrentDailyChess(username, options, callback, headers) { 39 | const [_options, _callback, _headers] = sortParameters( 40 | ['object', 'function', 'object'], 41 | [options, callback, headers], 42 | ); 43 | 44 | return WebApiRequest.builder() 45 | .withPath(`/pub/player/${username}/games`) 46 | .withQueryParameters(_options) 47 | .withHeaders(_headers) 48 | .build() 49 | .execute(HttpManager.get, _callback); 50 | } 51 | 52 | function getPlayerToMoveDailyChess(username, options, callback, headers) { 53 | const [_options, _callback, _headers] = sortParameters( 54 | ['object', 'function', 'object'], 55 | [options, callback, headers], 56 | ); 57 | 58 | return WebApiRequest.builder() 59 | .withPath(`/pub/player/${username}/games/to-move`) 60 | .withQueryParameters(_options) 61 | .withHeaders(_headers) 62 | .build() 63 | .execute(HttpManager.get, _callback); 64 | } 65 | 66 | function getPlayerMonthlyArchives(username, options, callback, headers) { 67 | const [_options, _callback, _headers] = sortParameters( 68 | ['object', 'function', 'object'], 69 | [options, callback, headers], 70 | ); 71 | 72 | return WebApiRequest.builder() 73 | .withPath(`/pub/player/${username}/games/archives`) 74 | .withQueryParameters(_options) 75 | .withHeaders(_headers) 76 | .build() 77 | .execute(HttpManager.get, _callback); 78 | } 79 | 80 | function getPlayerCompleteMonthlyArchives(username, year, month, options, callback, headers) { 81 | const [_options, _callback, _headers] = sortParameters( 82 | ['object', 'function', 'object'], 83 | [options, callback, headers], 84 | ); 85 | 86 | let _month; 87 | if ((typeof month === 'number' && month < 10) 88 | || (typeof month === 'string' && month.length === 1)) { 89 | _month = `0${month}`; 90 | } else { 91 | _month = `${month}`; 92 | } 93 | 94 | return WebApiRequest.builder() 95 | .withPath(`/pub/player/${username}/games/${year}/${_month}`) 96 | .withQueryParameters(_options) 97 | .withHeaders(_headers) 98 | .build() 99 | .execute(HttpManager.get, _callback); 100 | } 101 | 102 | function getPlayerMultiGamePGN(username, year, month, options, callback, headers) { 103 | const [_options, _callback, _headers] = sortParameters( 104 | ['object', 'function', 'object'], 105 | [options, callback, headers], 106 | ); 107 | 108 | let _month; 109 | if ((typeof month === 'number' && month < 10) 110 | || (typeof month === 'string' && month.length === 1)) { 111 | _month = `0${month}`; 112 | } else { 113 | _month = `${month}`; 114 | } 115 | 116 | return WebApiRequest.builder() 117 | .withPath(`/pub/player/${username}/games/${year}/${_month}/pgn`) 118 | .withQueryParameters(_options) 119 | .withHeaders(_headers) 120 | .build() 121 | .execute(HttpManager.get, _callback); 122 | } 123 | 124 | function getPlayerClubs(username, options, callback, headers) { 125 | const [_options, _callback, _headers] = sortParameters( 126 | ['object', 'function', 'object'], 127 | [options, callback, headers], 128 | ); 129 | 130 | return WebApiRequest.builder() 131 | .withPath(`/pub/player/${username}/clubs`) 132 | .withQueryParameters(_options) 133 | .withHeaders(_headers) 134 | .build() 135 | .execute(HttpManager.get, _callback); 136 | } 137 | 138 | function getPlayerMatches(username, options, callback, headers) { 139 | const [_options, _callback, _headers] = sortParameters( 140 | ['object', 'function', 'object'], 141 | [options, callback, headers], 142 | ); 143 | 144 | return WebApiRequest.builder() 145 | .withPath(`/pub/player/${username}/matches`) 146 | .withQueryParameters(_options) 147 | .withHeaders(_headers) 148 | .build() 149 | .execute(HttpManager.get, _callback); 150 | } 151 | 152 | function getPlayerTournaments(username, options, callback, headers) { 153 | const [_options, _callback, _headers] = sortParameters( 154 | ['object', 'function', 'object'], 155 | [options, callback, headers], 156 | ); 157 | 158 | return WebApiRequest.builder() 159 | .withPath(`/pub/player/${username}/tournaments`) 160 | .withQueryParameters(_options) 161 | .withHeaders(_headers) 162 | .build() 163 | .execute(HttpManager.get, _callback); 164 | } 165 | 166 | function getTitledPlayers(titleAbbrev, options, callback, headers) { 167 | const [_options, _callback, _headers] = sortParameters( 168 | ['object', 'function', 'object'], 169 | [options, callback, headers], 170 | ); 171 | 172 | return WebApiRequest.builder() 173 | .withPath(`/pub/titled/${titleAbbrev}`) 174 | .withQueryParameters(_options) 175 | .withHeaders(_headers) 176 | .build() 177 | .execute(HttpManager.get, _callback); 178 | } 179 | 180 | module.exports = { 181 | getPlayer, 182 | getPlayerStats, 183 | getPlayerOnline, 184 | getPlayerCurrentDailyChess, 185 | getPlayerToMoveDailyChess, 186 | getPlayerMonthlyArchives, 187 | getPlayerCompleteMonthlyArchives, 188 | getPlayerMultiGamePGN, 189 | getPlayerClubs, 190 | getPlayerMatches, 191 | getPlayerTournaments, 192 | getTitledPlayers, 193 | }; 194 | -------------------------------------------------------------------------------- /tests/player-data.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { 3 | getPlayer, 4 | getPlayerStats, 5 | getPlayerOnline, 6 | getPlayerCurrentDailyChess, 7 | getPlayerToMoveDailyChess, 8 | getPlayerMonthlyArchives, 9 | getPlayerCompleteMonthlyArchives, 10 | getPlayerMultiGamePGN, 11 | getPlayerClubs, 12 | getPlayerMatches, 13 | getPlayerTournaments, 14 | getTitledPlayers, 15 | } = require('../src/endpoints/player-data'); 16 | 17 | const USERNAME = 'andyruwruw'; 18 | 19 | describe('Endpoints: Player', () => { 20 | describe('getPlayer', () => { 21 | it('Valid Request', async () => { 22 | try { 23 | expect.assertions(5); 24 | const data = await getPlayer(USERNAME); 25 | 26 | expect(data.statusCode).toEqual(200); 27 | expect(data.body).toHaveProperty('player_id'); 28 | expect(data.body).toHaveProperty('url'); 29 | expect(data.body).toHaveProperty('name'); 30 | expect(data.body).toHaveProperty('username'); 31 | } catch (error) { 32 | console.log(error); 33 | } 34 | }); 35 | }); 36 | 37 | describe('getPlayerStats', () => { 38 | it('Valid Request', async () => { 39 | try { 40 | expect.assertions(4); 41 | const data = await getPlayerStats(USERNAME); 42 | 43 | expect(data.statusCode).toEqual(200); 44 | expect(data.body).toHaveProperty('chess_daily'); 45 | expect(data.body).toHaveProperty('chess_rapid'); 46 | expect(data.body).toHaveProperty('chess_blitz'); 47 | } catch (error) { 48 | console.log(error); 49 | } 50 | }); 51 | }); 52 | 53 | describe('getPlayerOnline', () => { 54 | it('Valid Request', async () => { 55 | try { 56 | // eslint-disable-next-line no-unused-vars 57 | const data = await getPlayerOnline(USERNAME); 58 | } catch (error) { 59 | expect(error.message).toBe('This endpoint was removed by Chess.com, please see https://github.com/andyruwruw/chess-web-api/tree/master#getplayeronlineusername-options-callback'); 60 | } 61 | }); 62 | }); 63 | 64 | describe('getPlayerCurrentDailyChess', () => { 65 | it('Valid Request', async () => { 66 | try { 67 | expect.assertions(3); 68 | const data = await getPlayerCurrentDailyChess(USERNAME); 69 | 70 | expect(data.statusCode).toEqual(200); 71 | expect(data.body).toHaveProperty('games'); 72 | expect(data.body.games).toBeInstanceOf(Array); 73 | } catch (error) { 74 | console.log(error); 75 | } 76 | }); 77 | }); 78 | 79 | describe('getPlayerToMoveDailyChess', () => { 80 | it('Valid Request', async () => { 81 | try { 82 | expect.assertions(3); 83 | const data = await getPlayerToMoveDailyChess(USERNAME); 84 | 85 | expect(data.statusCode).toEqual(200); 86 | expect(data.body).toHaveProperty('games'); 87 | expect(data.body.games).toBeInstanceOf(Array); 88 | } catch (error) { 89 | console.log(error); 90 | } 91 | }); 92 | }); 93 | 94 | describe('getPlayerMonthlyArchives', () => { 95 | it('Valid Request', async () => { 96 | try { 97 | expect.assertions(3); 98 | const data = await getPlayerMonthlyArchives(USERNAME); 99 | 100 | expect(data.statusCode).toEqual(200); 101 | expect(data.body).toHaveProperty('archives'); 102 | expect(data.body.archives).toBeInstanceOf(Array); 103 | } catch (error) { 104 | console.log(error); 105 | } 106 | }); 107 | }); 108 | 109 | describe('getPlayerCompleteMonthlyArchives', () => { 110 | it('Valid Request', async () => { 111 | try { 112 | expect.assertions(3); 113 | const data = await getPlayerCompleteMonthlyArchives(USERNAME, 2020, 8); 114 | 115 | expect(data.statusCode).toEqual(200); 116 | expect(data.body).toHaveProperty('games'); 117 | expect(data.body.games).toBeInstanceOf(Array); 118 | } catch (error) { 119 | console.log(error); 120 | } 121 | }); 122 | }); 123 | 124 | describe('getPlayerMultiGamePGN', () => { 125 | it('Valid Request', async () => { 126 | try { 127 | expect.assertions(2); 128 | const data = await getPlayerMultiGamePGN(USERNAME, 2020, 8); 129 | 130 | expect(data.statusCode).toEqual(200); 131 | expect(data.body).toBeInstanceOf(Buffer); 132 | } catch (error) { 133 | console.log(error); 134 | } 135 | }); 136 | }); 137 | 138 | describe('getPlayerClubs', () => { 139 | it('Valid Request', async () => { 140 | try { 141 | expect.assertions(3); 142 | const data = await getPlayerClubs(USERNAME); 143 | 144 | expect(data.statusCode).toEqual(200); 145 | expect(data.body).toHaveProperty('clubs'); 146 | expect(data.body.clubs).toBeInstanceOf(Array); 147 | } catch (error) { 148 | console.log(error); 149 | } 150 | }); 151 | }); 152 | 153 | describe('getPlayerMatches', () => { 154 | it('Valid Request', async () => { 155 | try { 156 | expect.assertions(5); 157 | const data = await getPlayerMatches(USERNAME); 158 | 159 | expect(data.statusCode).toEqual(200); 160 | expect(data.body).toHaveProperty('finished'); 161 | expect(data.body).toHaveProperty('in_progress'); 162 | expect(data.body).toHaveProperty('registered'); 163 | expect(data.body.finished).toBeInstanceOf(Array); 164 | } catch (error) { 165 | console.log(error); 166 | } 167 | }); 168 | }); 169 | 170 | describe('getPlayerTournaments', () => { 171 | it('Valid Request', async () => { 172 | try { 173 | expect.assertions(5); 174 | const data = await getPlayerTournaments(USERNAME); 175 | 176 | expect(data.statusCode).toEqual(200); 177 | expect(data.body).toHaveProperty('finished'); 178 | expect(data.body).toHaveProperty('in_progress'); 179 | expect(data.body).toHaveProperty('registered'); 180 | expect(data.body.finished).toBeInstanceOf(Array); 181 | } catch (error) { 182 | console.log(error); 183 | } 184 | }); 185 | }); 186 | 187 | describe('getTitledPlayers', () => { 188 | it('Valid Request', async () => { 189 | try { 190 | expect.assertions(3); 191 | const data = await getTitledPlayers('GM'); 192 | 193 | expect(data.statusCode).toEqual(200); 194 | expect(data.body).toHaveProperty('players'); 195 | expect(data.body.players).toBeInstanceOf(Array); 196 | } catch (error) { 197 | console.log(error); 198 | } 199 | }); 200 | }); 201 | }); 202 | -------------------------------------------------------------------------------- /tests/games.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const { Chess } = require('chess.js'); 3 | const each = require('jest-each').default; 4 | 5 | const { getGameByID } = require('../src/endpoints/games'); 6 | 7 | // simple game 8 | // API: https://www.chess.com/callback/live/game/6592985978 9 | // WEB: https://www.chess.com/analysis/game/live/6592985978 10 | const TEST_GAME_1 = [ 11 | // id, startFen, endFen 12 | '6592985978', 13 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 14 | 'r3k3/2p5/1pn3rn/p6p/1P1bPB1q/P2N3P/N1Q1K3/5B2 w q - 2 24', 15 | ]; 16 | 17 | // long game 18 | // API: https://www.chess.com/callback/live/game/6508402266 19 | // WEB: https://www.chess.com/analysis/game/live/6508402266 20 | const TEST_GAME_2 = [ 21 | // id, startFen, endFen 22 | '6508402266', 23 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 24 | 'K1r5/p5kp/8/6PP/8/1r6/8/8 w - - 9 56', 25 | ]; 26 | 27 | // promotion to Knight 28 | // API: https://www.chess.com/callback/live/game/7779158529 29 | // WEB: https://www.chess.com/analysis/game/live/7779158529 30 | const TEST_GAME_3 = [ 31 | // id, startFen, endFen 32 | '7779158529', 33 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 34 | 'r1bq1Nnr/pp1pk2p/n1p3pb/4p3/P4P2/1P1P4/2P3PP/RNBQKBNR b KQ a3 0 9', 35 | ]; 36 | 37 | // promotion to Knight, capturing left 38 | // API: https://www.chess.com/callback/live/game/7781476647 39 | // WEB: https://www.chess.com/analysis/game/live/7781476647 40 | const TEST_GAME_4 = [ 41 | // id, startFen, endFen 42 | '7781476647', 43 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 44 | 'rnbN1rk1/1p3pbp/p1p3pn/4P3/1P1P4/8/P4PPP/RNBQKBNR b KQ - 0 9', 45 | ]; 46 | 47 | // promotion to Knight, capturing right 48 | // API: https://www.chess.com/callback/live/game/7782722623 49 | // WEB: https://www.chess.com/analysis/game/live/7782722623 50 | const TEST_GAME_5 = [ 51 | // id, startFen, endFen 52 | '7782722623', 53 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 54 | 'rnbNkb1r/pp1ppppp/7n/8/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 5', 55 | ]; 56 | 57 | // promotion to Queen 58 | // API: https://www.chess.com/callback/live/game/7766570889 59 | // WEB: https://www.chess.com/analysis/game/live/7766570889 60 | const TEST_GAME_6 = [ 61 | // id, startFen, endFen 62 | '7766570889', 63 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 64 | 'rnbQ1bnr/pp2pkpp/2p5/q4p2/8/5P2/PPPP2PP/RNBQKBNR b KQ - 0 6', 65 | ]; 66 | 67 | // promotion to Queen, capturing left 68 | // API: https://www.chess.com/callback/live/game/7791144955 69 | // WEB: https://www.chess.com/analysis/game/live/7791144955 70 | const TEST_GAME_7 = [ 71 | // id, startFen, endFen 72 | '7791144955', 73 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 74 | 'rnbqkbnr/p2ppppp/8/1p6/P7/8/2PPPPPP/qNBQKBNR w Kkq - 0 7', 75 | ]; 76 | 77 | // promotion to Queen, capturing right 78 | // API: https://www.chess.com/callback/live/game/7783310939 79 | // WEB: https://www.chess.com/analysis/game/live/7783310939 80 | const TEST_GAME_8 = [ 81 | // id, startFen, endFen 82 | '7783310939', 83 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 84 | 'rnbQkb1r/pp1ppppp/7n/8/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 5', 85 | ]; 86 | 87 | // promotion to Bishop `#` 88 | // API: https://www.chess.com/callback/live/game/7766500765 89 | // WEB: https://www.chess.com/analysis/game/live/7766500765 90 | const TEST_GAME_9 = [ 91 | // id, startFen, endFen 92 | '7766500765', 93 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 94 | 'rnbqkbnr/ppp1pppp/8/8/8/1QP5/PPKP1PPP/RNB1bBNR w kq - 0 6', 95 | ]; 96 | 97 | // promotion to Bishop, capturing left 98 | // API: https://www.chess.com/callback/live/game/7782109487 99 | // WEB: https://www.chess.com/analysis/game/live/7782109487 100 | const TEST_GAME_10 = [ 101 | // id, startFen, endFen 102 | '7782109487', 103 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 104 | 'rnbBkb1r/ppp2ppp/7n/3p4/4P3/8/PPP2PPP/RNBQKBNR b KQkq - 0 6', 105 | ]; 106 | 107 | // promotion to Bishop, capturing right 108 | // API: https://www.chess.com/callback/live/game/7782766035 109 | // WEB: https://www.chess.com/analysis/game/live/7782766035 110 | const TEST_GAME_11 = [ 111 | // id, startFen, endFen 112 | '7782766035', 113 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 114 | 'rnbBkb1r/pp1ppppp/7n/8/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 5', 115 | ]; 116 | 117 | // promotion to Rook 118 | // API: https://www.chess.com/callback/live/game/7766621831 119 | // WEB: https://www.chess.com/analysis/game/live/7766621831 120 | const TEST_GAME_12 = [ 121 | // id, startFen, endFen 122 | '7766621831', 123 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 124 | 'rnbqkbnr/ppp1pppp/8/7Q/5P2/3P4/PPPK2PP/RNB1rBNR w kq - 0 6', 125 | ]; 126 | 127 | // promotion to Rook, capturing left 128 | // API: https://www.chess.com/callback/live/game/7782154707 129 | // WEB: https://www.chess.com/analysis/game/live/7782154707 130 | const TEST_GAME_13 = [ 131 | // id, startFen, endFen 132 | '7782154707', 133 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 134 | 'rnbRkb1r/ppp2ppp/7n/3p4/2P5/8/PP2PPPP/RNBQKBNR b KQkq - 0 6', 135 | ]; 136 | 137 | // promotion to Rook, capturing right 138 | // API: https://www.chess.com/callback/live/game/7783381153 139 | // WEB: https://www.chess.com/analysis/game/live/7783381153 140 | const TEST_GAME_14 = [ 141 | // id, startFen, endFen 142 | '7783381153', 143 | 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 144 | 'rnbRkb1r/pp1ppppp/7n/8/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 5', 145 | ]; 146 | 147 | const ALL_TEST_GAMES = [ 148 | TEST_GAME_1, 149 | TEST_GAME_2, 150 | TEST_GAME_3, 151 | TEST_GAME_4, 152 | TEST_GAME_5, 153 | TEST_GAME_6, 154 | TEST_GAME_7, 155 | TEST_GAME_8, 156 | TEST_GAME_9, 157 | TEST_GAME_10, 158 | TEST_GAME_11, 159 | TEST_GAME_12, 160 | TEST_GAME_13, 161 | TEST_GAME_14, 162 | ]; 163 | 164 | describe('Endpoints: Games', () => { 165 | describe('getGameByID', () => { 166 | each(ALL_TEST_GAMES).it('Valid Request', async (id, startFen, endFen) => { 167 | try { 168 | expect.assertions(6); 169 | const data = await getGameByID(id); 170 | 171 | expect(data.statusCode).toEqual(200); 172 | expect(data.body).toHaveProperty('game'); 173 | expect(data.body).toHaveProperty('game.moveList'); 174 | expect(data.body).toHaveProperty('game.pgnHeaders'); 175 | expect(data.body).toHaveProperty('game.pgn'); 176 | 177 | // verify that PGN results in the expected FEN per the game analysis page 178 | const chess = new Chess(startFen); 179 | chess.load_pgn(data.body.game.pgn); 180 | expect(chess.fen()).toEqual(endFen); 181 | } catch (error) { 182 | console.log(error); 183 | } 184 | }); 185 | }); 186 | }); 187 | -------------------------------------------------------------------------------- /src/endpoints/games.js: -------------------------------------------------------------------------------- 1 | const { Chess } = require('chess.js'); 2 | 3 | const WebApiRequest = require('../request/webapi-request.js'); 4 | const HttpManager = require('../request/http-manager.js'); 5 | const WebApiError = require('../request/webapi-error'); 6 | const { sortParameters } = require('../utils/sort-parameters'); 7 | 8 | // see: https://github.com/andyruwruw/chess-web-api/issues/10#issuecomment-779735204 9 | const BOARD_POSITIONS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!?'; 10 | const BOARD_FILES = 'abcdefgh'; 11 | const BOARD_LENGTH = 8; 12 | 13 | // see: https://github.com/andyruwruw/chess-web-api/issues/11#issuecomment-783687021 14 | const PROMOTION_TABLE = '#@$_[]^()~{}'; 15 | const PROMOTION_TABLE_ROWS = 'brnq'; 16 | const PROMOTION_TABLE_ROWS_LENGTH = 4; 17 | const PROMOTION_TABLE_COLUMNS_LENGTH = 3; 18 | const PROMOTION_CAPTURE_LEFT = 1; 19 | const PROMOTION_CAPTURE_RIGHT = 2; 20 | 21 | const UNDOCUMENTED_API_HOST = 'chess.com'; 22 | const ERR_MISSING_OR_EMPTY = 'Result missing or empty.'; 23 | 24 | /** 25 | * _PawnPromotion constructor 26 | * 27 | * @param {*} index 28 | */ 29 | function _PawnPromotion(index) { 30 | const pieceIndex = Math.floor(index / PROMOTION_TABLE_COLUMNS_LENGTH); 31 | if (pieceIndex > PROMOTION_TABLE_ROWS_LENGTH - 1) { 32 | // this can only happen if the const table values are wrong 33 | throw new Error(`Pawn promotion row index out of bounds: ${pieceIndex}`); 34 | } 35 | this.piece = PROMOTION_TABLE_ROWS[pieceIndex]; 36 | this.isCaptureLeft = index % PROMOTION_TABLE_COLUMNS_LENGTH 37 | === PROMOTION_CAPTURE_LEFT; 38 | this.isCaptureRight = index % PROMOTION_TABLE_COLUMNS_LENGTH 39 | === PROMOTION_CAPTURE_RIGHT; 40 | } 41 | 42 | /** 43 | * Calculate the destination square of a pawn promotion 44 | * 45 | * @param {*} from 46 | */ 47 | _PawnPromotion.prototype.getTo = function (from) { 48 | const fromFile = from[0]; 49 | const fromRank = from[1]; 50 | const fromFileIndex = BOARD_FILES.indexOf(fromFile); 51 | 52 | let toFileIndex; 53 | if (this.isCaptureLeft) { 54 | toFileIndex = fromFileIndex - 1; 55 | } else { 56 | toFileIndex = this.isCaptureRight 57 | ? fromFileIndex + 1 58 | : fromFileIndex; 59 | } 60 | 61 | // sanity check: ensure pawn is still on the board after promotion 62 | if (toFileIndex < 0 || toFileIndex > BOARD_LENGTH - 1) { 63 | throw new Error(`Invalid pawn promotion; file index out of bounds: ${toFileIndex}`); 64 | } 65 | const toFile = BOARD_FILES[toFileIndex]; 66 | 67 | // sanity check: ensure pawn rank is 2 or 7 prior to promotion 68 | let toRank; 69 | if (fromRank === '2') { 70 | toRank = '1'; 71 | } else if (fromRank === '7') { 72 | toRank = '8'; 73 | } else { 74 | throw new Error(`Invalid rank prior to pawn promotion: ${fromRank}`); 75 | } 76 | return `${toFile}${toRank}`; 77 | }; 78 | 79 | /** 80 | * Decode a move-character into algebraic notation or pawn promotion 81 | * 82 | * @param {*} encMove 83 | * @param {*} isTo 84 | */ 85 | function _decodeMove(encMove, isTo) { 86 | const index = BOARD_POSITIONS.indexOf(encMove); 87 | if (index === -1) { 88 | // if this is the "to" field, check for pawn promotion 89 | if (isTo) { 90 | const promotionIndex = PROMOTION_TABLE.indexOf(encMove); 91 | if (promotionIndex !== -1) { 92 | return new _PawnPromotion(promotionIndex); 93 | } 94 | } 95 | throw new Error(`Unrecognized move-character: ${encMove}`); 96 | } 97 | const file = BOARD_FILES[index % BOARD_LENGTH]; 98 | const rank = Math.floor(index / BOARD_LENGTH) + 1; 99 | return `${file}${rank}`; 100 | } 101 | 102 | /** 103 | * Generate a PGN string using chess.js 104 | */ 105 | function _getPGN(pgnHeaders, moveList) { 106 | const moveListLength = moveList.length; 107 | if (moveListLength === 0 || moveListLength % 2 !== 0) { 108 | throw new Error('Malformed field "game.moveList"; ' 109 | + `expected non-empty, even-number of characters: ${moveList}`); 110 | } 111 | const chess = 'FEN' in pgnHeaders 112 | ? new Chess(pgnHeaders.FEN) 113 | : new Chess(); 114 | 115 | Object.keys(pgnHeaders).forEach((key) => { 116 | chess.header(key, pgnHeaders[key]); 117 | }); 118 | 119 | for (let i = 0; i < moveListLength; i += 2) { 120 | const move = { 121 | from: _decodeMove(moveList[i], false), 122 | to: _decodeMove(moveList[i + 1], true), 123 | }; 124 | if (move.to instanceof _PawnPromotion) { 125 | move.promotion = move.to.piece; 126 | move.to = move.to.getTo(move.from); 127 | } 128 | chess.move(move); 129 | } 130 | return chess.pgn(); 131 | } 132 | 133 | /** 134 | * Get an object property, throwing an Error if missing 135 | * 136 | * @param {*} obj 137 | * @param {*} propName 138 | * @param {*} fullName 139 | */ 140 | function _getRequiredProperty(obj, propName, fullName) { 141 | if (Object.prototype.hasOwnProperty.call(obj, propName)) { 142 | return obj[propName]; 143 | } 144 | const fieldName = typeof fullName !== 'undefined' ? fullName : propName; 145 | throw new Error(`Missing required field "${fieldName}"`); 146 | } 147 | 148 | /** 149 | * Add the `game.pgn` field to the API results body. 150 | * Required fields: `game.moveList`, `game.pgnHeaders` 151 | * 152 | * @param {*} resultBody 153 | */ 154 | function _modifyResultBody(resultBody) { 155 | const game = _getRequiredProperty(resultBody, 'game'); 156 | const moveList = _getRequiredProperty(game, 'moveList', 'game.moveList'); 157 | const pgnHeaders = _getRequiredProperty(game, 'pgnHeaders', 'game.pgnHeaders'); 158 | game.pgn = _getPGN(pgnHeaders, moveList); 159 | } 160 | 161 | /** 162 | * Lookup game by ID, generating a PGN on-the-fly 163 | * 164 | * @param {*} id 165 | * @param {*} options 166 | * @param {*} callback 167 | * @param {*} headers 168 | */ 169 | function getGameByID(id, options, callback, headers) { 170 | const [_options, _callback, _headers] = sortParameters( 171 | ['object', 'function', 'object'], 172 | [options, callback, headers], 173 | 174 | ); 175 | 176 | // construct the API request but do not execute it directly, as we need 177 | // to modify the result body 178 | const apiRequest = WebApiRequest.builder() 179 | .withHost(UNDOCUMENTED_API_HOST) 180 | .withPath(`/callback/live/game/${id}`) 181 | .withQueryParameters(_options) 182 | .withHeaders(_headers) 183 | .build(); 184 | 185 | // properties of the result object: {body, headers, statusCode} 186 | if (_callback) { 187 | HttpManager.get(apiRequest, (error, result) => { 188 | if (error) { 189 | _callback(error); 190 | } else if (!result || !Object.prototype.hasOwnProperty.call(result, 'body')) { 191 | _callback(new WebApiError(ERR_MISSING_OR_EMPTY)); 192 | } else { 193 | try { 194 | _modifyResultBody(result.body); 195 | _callback(null, result); 196 | } catch (e) { 197 | _callback(e); 198 | } 199 | } 200 | }); 201 | return null; 202 | } 203 | 204 | return new Promise(((resolve, reject) => { 205 | HttpManager.get(apiRequest, (error, result) => { 206 | if (error) { 207 | reject(error); 208 | } else if (!result || !Object.prototype.hasOwnProperty.call(result, 'body')) { 209 | reject(new WebApiError(ERR_MISSING_OR_EMPTY)); 210 | } else { 211 | try { 212 | _modifyResultBody(result.body); 213 | resolve(result); 214 | } catch (e) { 215 | reject(e); 216 | } 217 | } 218 | }); 219 | })); 220 | } 221 | 222 | module.exports = { 223 | getGameByID, 224 | }; 225 | -------------------------------------------------------------------------------- /documentation/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |

Lightweight wrapper for the Chess.com public data API

6 | 7 |

8 | 9 | 10 |

11 | 12 | # Overview 13 | 14 | [chess-web-api](https://www.npmjs.com/package/chess-web-api) is a lightweight, unofficial wrapper for the [Chess.com public data API](https://www.chess.com/news/view/published-data-api). 15 | 16 | It includes helper functions for all available endpoints, such as retrieving player data, current daily chess games, monthly archives, club data, tournaments and more. 17 | 18 | Please read Chess.com's notes on [data currency, language and rate limits on parallel requests](https://www.chess.com/news/view/published-data-api#pubapi-general-current) before you implement this into your work. 19 | 20 | I'll try to keep this library updated if they're API changes, feel free to [submit any issues](https://github.com/andyruwruw/chess-web-api/issues). 21 | 22 | All endpoint descriptions were copied from [here](https://www.chess.com/news/view/published-data-api). It's here for ease of access only. Please go to the site to ensure the return values have not been changed. 23 | 24 | # Documentation 25 | 26 | - [Installation](#installation) 27 | - Methods 28 | - Player 29 | - Profile Data 30 | - [Player Profile](#getplayerusername-options-callback) 31 | - [Player Stats](#getplayerstatsusername-options-callback) 32 | - [Player Online Status](#getplayeronlineusername-options-callback) 33 | - Player Games 34 | - [Current Daily Chess](#getplayercurrentdailychessusername-options-callback) 35 | - [Concise To-Move Daily Chess](#getplayertomovedailychessusername-options-callback) 36 | - [Available Archives](#getplayermonthlyarchivesusername-options-callback) 37 | - [Monthly Archives](#getplayercompletemonthlyarchivesusername-year-month-options-callback) 38 | - [Multi-Game PGN Download](#getplayermultigamepgnusername-year-month-options-callback) 39 | - Player Participation 40 | - [List of Clubs](#getplayerclubsusername-options-callback) 41 | - [Team Matches](#getplayermatchesusername-options-callback) 42 | - [Tournaments](#getplayertournamentsusername-options-callback) 43 | - Games 44 | - [Game Data by ID](#getgamebyidid-options-callback) 45 | - Clubs 46 | - [Club Profile](#getcluburlid-options-callback) 47 | - [List of members, by activity level](#getclubmembersurlid-options-callback) 48 | - [Team Matches](#getclubmatchesurlid-options-callback) 49 | - Tournaments 50 | - [Tournament](#gettournamenturlid-options-callback) 51 | - [Tournament Round](#gettournamentroundurlid-round-options-callback) 52 | - [Tournament Round's Group](#gettournamentroundgroupurlid-round-group-options-callback) 53 | - Team Matches 54 | - [Daily Team Match](#getteammatchid-options-callback) 55 | - [Daily Team Match Board](#getteammatchboardid-board-options-callback) 56 | - [Live Team Match](#getteamlivematchid-options-callback) 57 | - [Live Team Match Board](#getteamlivematchboardid-board-options-callback) 58 | - Countries 59 | - [Country Profile](#getcountryiso-options-callback) 60 | - [List of Players](#getcountryplayersiso-options-callback) 61 | - [List of Clubs](#getcountryclubsiso-options-callback) 62 | - Daily Puzzle 63 | - [Daily Puzzle](#getdailypuzzleoptions-callback) 64 | - [Random Daily Puzzle](#getdailypuzzlerandomoptions-callback) 65 | - General 66 | - [Streamers](#getstreamersoptions-callback) 67 | - [Leaderboards](#getleaderboardsoptions-callback) 68 | - [Titled Players](#gettitledplayerstitleabbrev-options-callback) 69 | - [Priority Queue](#priority-queue) 70 | - [dispatch](#----dispatchmethod-callback-parameters-options-callbackparameters-priority) 71 | - [clearQueue](#clearqueue) 72 | - [Query for Changes](#query-for-changes) 73 | - [ifChanged](#------------ifchangedetag-method-parameters-options-callback----) 74 | 75 | # Installation 76 | 77 | Install via node: 78 | 79 | $ npm i chess-web-api 80 | 81 | Import the module and instantiate the wrapper. 82 | ``` 83 | var ChessWebAPI = require('chess-web-api'); 84 | 85 | var chessAPI = new ChessWebAPI(); 86 | ``` 87 | 88 | You are then free to call any of the methods! 89 | 90 | ``` 91 | chessAPI.getPlayer('andyruwruw') 92 | .then(function(response) { 93 | console.log('Player Profile', response.body); 94 | }, function(err) { 95 | console.error(err); 96 | }); 97 | ``` 98 | 99 | # [getPlayer(username, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-player) 100 | Get additional details about a player in a game. 101 | ### Parameters 102 | | Name | Type | Description | 103 | |----------|------------|------------------------------------------------| 104 | | username | **string** | Username of desired profile. | 105 | | options | **object** | Added options to the end of the URL (optional) | 106 | | callback | **function** | Function to be called with result (optional) | 107 | 108 | # [getPlayerStats(username, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-player-stats) 109 | Get ratings, win/loss, and other stats about a player's game play, tactics, lessons and Puzzle Rush score. 110 | ### Parameters 111 | | Name | Type | Description | 112 | |----------|------------|------------------------------------------------| 113 | | username | **string** | Username of desired profile. | 114 | | options | **object** | Added options to the end of the URL (optional) | 115 | | callback | **function** | Function to be called with result (optional) | 116 | 117 | # [getPlayerOnline(username, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-player-is-online) 118 | 119 | This endpoint was removed by Chess.com 8/25/2021. For more information see the [forum post](https://www.chess.com/clubs/forum/view/api-rfc-deprecate-and-remove-is-online-endpoint). 120 | 121 | ``` 122 | This endpoint requires a large number of internal resources to maintain, and in the past week it has been used by only one clear developer script with a user-agent that helps us contact the developer. All other access to that endpoint appears to be tests, runaway scripts, or unidentified programs that could work better. 123 | ``` 124 | 125 | For those looking for a simular endpoint, [Player Profile](#getplayerusername-options-callback) includes a **last_online** field. 126 | 127 | # [getPlayerCurrentDailyChess(username, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-games-current) 128 | Array of Daily Chess games that a player is currently playing. 129 | ### Parameters 130 | | Name | Type | Description | 131 | |----------|------------|------------------------------------------------| 132 | | username | **string** | Username of desired profile. | 133 | | options | **object** | Added options to the end of the URL (optional) | 134 | | callback | **function** | Function to be called with result (optional) | 135 | 136 | # [getPlayerToMoveDailyChess(username, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-games-tomove) 137 | Array of Daily Chess games where it is the player's turn to act. 138 | ### Parameters 139 | | Name | Type | Description | 140 | |----------|------------|------------------------------------------------| 141 | | username | **string** | Username of desired profile. | 142 | | options | **object** | Added options to the end of the URL (optional) | 143 | | callback | **function** | Function to be called with result (optional) | 144 | 145 | # [getPlayerMonthlyArchives(username, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-games-archive-list) 146 | Array of monthly archives available for this player. 147 | ### Parameters 148 | | Name | Type | Description | 149 | |----------|------------|------------------------------------------------| 150 | | username | **string** | Username of desired profile. | 151 | | options | **object** | Added options to the end of the URL (optional) | 152 | | callback | **function** | Function to be called with result (optional) | 153 | 154 | # [getPlayerCompleteMonthlyArchives(username, year, month, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-games-archive) 155 | Array of Live and Daily Chess games that a player has finished. 156 | ### Parameters 157 | | Name | Type | Description | 158 | |----------|------------|------------------------------------------------| 159 | | username | **string** | Username of desired profile. | 160 | | year | **string / number** | Year of matches. | 161 | | month | **string / number** | Month of matches. | 162 | | options | **object** | Added options to the end of the URL (optional) | 163 | | callback | **function** | Function to be called with result (optional) | 164 | 165 | # [getPlayerMultiGamePGN(username, year, month, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-games-pgn) 166 | Standard multi-game format PGN containing all games for a month. 167 | ### Parameters 168 | | Name | Type | Description | 169 | |----------|------------|------------------------------------------------| 170 | | username | **string** | Username of desired profile. | 171 | | year | **string / number** | Year of matches. | 172 | | month | **string / number** | Month of matches. | 173 | | options | **object** | Added options to the end of the URL (optional) | 174 | | callback | **function** | Function to be called with result (optional) | 175 | 176 | # [getPlayerClubs(username, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-player-clubs) 177 | List of clubs the player is a member of, with joined date and last activity date. 178 | ### Parameters 179 | | Name | Type | Description | 180 | |----------|------------|------------------------------------------------| 181 | | username | **string** | Username of desired profile. | 182 | | options | **object** | Added options to the end of the URL (optional) | 183 | | callback | **function** | Function to be called with result (optional) | 184 | 185 | # [getPlayerMatches(username, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-player-matches) 186 | List of Team matches the player has attended, is partecipating or is currently registered. 187 | ### Parameters 188 | | Name | Type | Description | 189 | |----------|------------|------------------------------------------------| 190 | | username | **string** | Username of desired profile. | 191 | | options | **object** | Added options to the end of the URL (optional) | 192 | | callback | **function** | Function to be called with result (optional) | 193 | 194 | # [getPlayerTournaments(username, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-player-tournaments) 195 | List of tournaments the player is registered, is attending or has attended in the past. 196 | ### Parameters 197 | | Name | Type | Description | 198 | |----------|------------|------------------------------------------------| 199 | | username | **string** | Username of desired profile. | 200 | | options | **object** | Added options to the end of the URL (optional) | 201 | | callback | **function** | Function to be called with result (optional) | 202 | 203 | # [getGameByID(id, options, callback)](https://github.com/andyruwruw/chess-web-api/blob/master/documentation/GAME.md) 204 | 205 | Game data lookup by ID. Includes PGN, metadata, and player info. 206 | 207 | For more information on what fields this generates or how it was implemented, [see here](https://github.com/andyruwruw/chess-web-api/blob/master/documentation/GAME.md). 208 | 209 | ### **Read Before Using**: Fair Usage 210 | 211 | chess-web-api's `getGameById` method is not an official endpoint of [Chess.com's Published Data API](https://www.chess.com/news/view/published-data-api). It uses a callback from Chess.com's website to get its data. 212 | 213 | Therefore it is highly unstable and could be changed without warning or stop functioning. Hammering this endpoint with requests could result in an IP ban from Chess.com. This would not only impact your script, but your access to Chess.com itself. 214 | 215 | The use of this endpoint violates Chess.com's Terms of Service, however Chess.com has informed me that as long as the "script is polite", they have no issue and it will go unnoticed. Corrective action will be taken if there is abuse. 216 | 217 | There is work to add a simular endpoint to Chess.com, and this library will be updated when that takes place. There is no current timeline there and it could take up to a year. 218 | 219 | Please [submit an issue](https://github.com/andyruwruw/chess-web-api/issues) if you notice any strange behavior to improve this documentation or make fixes. I'll periodically check to make sure the endpoint is working as intended and make fixes when needed. 220 | 221 | ### Parameters 222 | | Name | Type | Description | 223 | |----------|--------------|------------------------------------------------| 224 | | id | **string** | The game ID. | 225 | | options | **object** | Added options to the end of the URL (optional) | 226 | | callback | **function** | Function to be called with result (optional) | 227 | 228 | # [getClub(urlID, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-club-profile) 229 | Get additional details about a club. 230 | 231 | All club-based URLs use the club's "URL ID" to specify which club you want data for. 232 | 233 | The url-ID is the same as found in the URL for the club's web page on www.chess.com. For example, the url-ID of the Chess.com Developer's Club is chess-com-developer-community. 234 | ### Parameters 235 | | Name | Type | Description | 236 | |----------|------------|------------------------------------------------| 237 | | urlID | **string** | Club's unique urlID | 238 | | options | **object** | Added options to the end of the URL (optional) | 239 | | callback | **function** | Function to be called with result (optional) | 240 | 241 | # [getClubMembers(urlID, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-club-members) 242 | List of club members (usernames and joined date timestamp), grouped by club-activity frequency. The club-activity is one of this actions. 243 | ### Parameters 244 | | Name | Type | Description | 245 | |----------|------------|------------------------------------------------| 246 | | urlID | **string** | Club's unique urlID | 247 | | options | **object** | Added options to the end of the URL (optional) | 248 | | callback | **function** | Function to be called with result (optional) | 249 | 250 | # [getClubMatches(urlID, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-club-matches) 251 | List of daily and club matches, grouped by status (registered, in progress, finished). 252 | ### Parameters 253 | | Name | Type | Description | 254 | |----------|------------|------------------------------------------------| 255 | | urlID | **string** | Club's unique urlID | 256 | | options | **object** | Added options to the end of the URL (optional) | 257 | | callback | **function** | Function to be called with result (optional) | 258 | 259 | # [getTournament(urlID, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-tournament-profile) 260 | Get details about a daily, live and arena tournament. 261 | 262 | All tournaments-based URLs use the tournament's "URL ID" to specify which tournament you want data for. 263 | 264 | The url-ID is the same as found in the URL for the tournament's web page on www.chess.com. For example, the url-ID of the Chess.com Developer's Club is -33rd-chesscom-quick-knockouts-1401-1600 265 | ### Parameters 266 | | Name | Type | Description | 267 | |----------|------------|------------------------------------------------| 268 | | urlID | **string** | Tournaments's unique urlID | 269 | | options | **object** | Added options to the end of the URL (optional) | 270 | | callback | **function** | Function to be called with result (optional) | 271 | 272 | # [getTournamentRound(urlID, round, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-tournament-round) 273 | Get details about a tournament's round. 274 | ### Parameters 275 | | Name | Type | Description | 276 | |----------|------------|------------------------------------------------| 277 | | urlID | **string** | Tournaments's unique urlID | 278 | | round | **string / number** | Round number | 279 | | options | **object** | Added options to the end of the URL (optional) | 280 | | callback | **function** | Function to be called with result (optional) | 281 | 282 | # [getTournamentRoundGroup(urlID, round, group, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-tournament-round-group) 283 | Get details about a tournament's round group. 284 | ### Parameters 285 | | Name | Type | Description | 286 | |----------|------------|------------------------------------------------| 287 | | urlID | **string** | Tournaments's unique urlID | 288 | | round | **string / number** | Round number | 289 | | group | **string / number** | Group number | 290 | | options | **object** | Added options to the end of the URL (optional) | 291 | | callback | **function** | Function to be called with result (optional) | 292 | 293 | # [getTeamMatch(id, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-match-profile) 294 | Get details about a team match and players playing that match. After the match is finished there will be a link to each player's stats endpoint, in order to get up-to-date information about the player. 295 | 296 | All team matches-based URLs use the match "ID" to specify which match you want data for. 297 | 298 | The ID is the same as found in the URL for the team match web page on www.chess.com. For example, the ID WORLD LEAGUE Round 5: Romania vs USA Southwest is 12803. 299 | ### Parameters 300 | | Name | Type | Description | 301 | |----------|------------|------------------------------------------------| 302 | | id | **string / number** | Id of desired team match. | 303 | | options | **object** | Added options to the end of the URL (optional) | 304 | | callback | **function** | Function to be called with result (optional) | 305 | 306 | # [getTeamMatchBoard(id, board, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-match-board) 307 | Get details about a team match board. Only in-progress or finished games will be included, so there may be one or two games in this list. 308 | ### Parameters 309 | | Name | Type | Description | 310 | |----------|------------|------------------------------------------------| 311 | | id | **string / number** | Id of desired team match. | 312 | | board | **string / number** | Board identifier | 313 | | options | **object** | Added options to the end of the URL (optional) | 314 | | callback | **function** | Function to be called with result (optional) | 315 | 316 | # [getTeamLiveMatch(id, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-match-live-profile) 317 | Get details about a team match and players playing that match. After the match is finished there will be a link to each player's stats endpoint, in order to get up-to-date information about the player. 318 | 319 | All live team matches-based URLs use the match "ID" to specify which match you want data for. 320 | 321 | The ID is the same as found in the URL for the team match web page on www.chess.com. For example, the ID Friendly 5+2 is 5833. 322 | ### Parameters 323 | | Name | Type | Description | 324 | |----------|------------|------------------------------------------------| 325 | | id | **string / number** | Id of desired live team match. | 326 | | options | **object** | Added options to the end of the URL (optional) | 327 | | callback | **function** | Function to be called with result (optional) | 328 | 329 | # [getTeamLiveMatchBoard(id, board, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-match-live-board) 330 | Get details about a team match board. Only in-progress or finished games will be included, so there may be one or two games in this list. 331 | ### Parameters 332 | | Name | Type | Description | 333 | |----------|------------|------------------------------------------------| 334 | | id | **string / number** | Id of desired live team match. | 335 | | board | **string / number** | Board identifier | 336 | | options | **object** | Added options to the end of the URL (optional) | 337 | | callback | **function** | Function to be called with result (optional) | 338 | 339 | # [getCountry(iso, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-country-profile) 340 | Get additional details about a country. 341 | 342 | All country-based URLs use the country's 2-character ISO 3166 code (capitalized) to specify which country you want data for. 343 | 344 | Find their [code's here](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). 345 | 346 | Additional countries not listed on that official list have been posted by [Chess.com](https://www.chess.com/news/view/published-data-api). 347 | ### Parameters 348 | | Name | Type | Description | 349 | |----------|------------|------------------------------------------------| 350 | | iso | **string** | Country's ISO identifier | 351 | | options | **object** | Added options to the end of the URL (optional) | 352 | | callback | **function** | Function to be called with result (optional) | 353 | 354 | # [getCountryPlayers(iso, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-country-players) 355 | List of usernames for players who identify themselves as being in this country. 356 | ### Parameters 357 | | Name | Type | Description | 358 | |----------|------------|------------------------------------------------| 359 | | iso | **string** | Country's ISO identifier | 360 | | options | **object** | Added options to the end of the URL (optional) | 361 | | callback | **function** | Function to be called with result (optional) | 362 | 363 | # [getCountryClubs(iso, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-country-clubs) 364 | List of URLs for clubs identified as being in or associated with this country. 365 | ### Parameters 366 | | Name | Type | Description | 367 | |----------|------------|------------------------------------------------| 368 | | iso | **string** | Country's ISO identifier | 369 | | options | **object** | Added options to the end of the URL (optional) | 370 | | callback | **function** | Function to be called with result (optional) | 371 | 372 | # [getDailyPuzzle(options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-daily-puzzle) 373 | Information about the daily puzzle found in [www.chess.com](www.chess.com). 374 | ### Parameters 375 | | Name | Type | Description | 376 | |----------|------------|------------------------------------------------| 377 | | options | **object** | Added options to the end of the URL (optional) | 378 | | callback | **function** | Function to be called with result (optional) | 379 | 380 | # [getDailyPuzzleRandom(options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-random-daily-puzzle) 381 | Information about a randomly picked daily puzzle. 382 | ### Parameters 383 | | Name | Type | Description | 384 | |----------|------------|------------------------------------------------| 385 | | options | **object** | Added options to the end of the URL (optional) | 386 | | callback | **function** | Function to be called with result (optional) | 387 | 388 | # [getStreamers(options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-streamers) 389 | Information about Chess.com streamers. 390 | ### Parameters 391 | | Name | Type | Description | 392 | |----------|------------|------------------------------------------------| 393 | | options | **object** | Added options to the end of the URL (optional) | 394 | | callback | **function** | Function to be called with result (optional) | 395 | 396 | # [getLeaderboards(options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-leaderboards) 397 | It displays information about top 50 player for daily and live games, tactics and lessons. 398 | ### Parameters 399 | | Name | Type | Description | 400 | |----------|------------|------------------------------------------------| 401 | | options | **object** | Added options to the end of the URL (optional) | 402 | | callback | **function** | Function to be called with result (optional) | 403 | 404 | # [getTitledPlayers(titleAbbrev, options, callback)](https://www.chess.com/news/view/published-data-api#pubapi-endpoint-titled) 405 | Retrieves an array of usernames of players with a given title. 406 | 407 | Valid title abbreviations are: GM, WGM, IM, WIM, FM, WFM, NM, WNM, CM, WCM. 408 | ### Parameters 409 | | Name | Type | Description | 410 | |----------|------------|------------------------------------------------| 411 | | titleAbbrev | **string** | GM, WGM, IM, WIM, FM, WFM, NM, WNM, CM, WCM.| 412 | | options | **object** | Added options to the end of the URL (optional) | 413 | | callback | **function** | Function to be called with result (optional) | 414 | 415 | 416 | # Priority Queue 417 | 418 | The Chess.com public data API does not allow you to make two requests simultaneously. If you have more than two active requests at a time, you'll recieve a **429 Too Many Requests** error. 419 | 420 | chess-web-api can be initialized with a priority queue. All requests made through the queue will be made as soon as the previous returns. 421 | 422 | To use chess-web-api with a queue, instantiate the wrapper with the following option set to true: 423 | ``` 424 | var ChessWebAPI = require('chess-web-api'); 425 | 426 | var chessAPI = new ChessWebAPI({ 427 | queue: true, 428 | }); 429 | ``` 430 | 431 | Using the queue requires the passing of a callback function as a parameter. 432 | 433 | To add something to the queue, use the method dispatch. 434 | 435 |

436 | dispatch(method, callback, parameters, options, callbackParameters, priority) 437 |

438 | 439 | Adds an item to the priority queue. 440 | 441 | The provided callback method will be provided two parameters **(response, error)**. Response is the full request result. 442 | 443 | Additional parameters can be passed to the callback in **callbackParameters**. 444 | 445 | The dispatch function determines the difference between parameters to be passed into the **chess-web-api method** vs the **callback function** by order. 446 | 447 | The **first array** passed into dispatch will always be passed into the **chess-web-api method**. 448 | 449 | The **second array** will always be sent to the **callback function**, with the response to the request as the first parameter. See example. 450 | 451 | ### Parameters 452 | | Name | Type | Description | 453 | |------------|------------|---------------------------------------------------| 454 | | method | **function** | **chess-web-api** function for request. | 455 | | callback | **function** | Function to be called with result | 456 | | parameters | **array** | Array of parameters to be passed into the method. | 457 | | options | **object** | Added options to the end of the URL (optional) | 458 | | callbackParameters | **array** | Array of parameters to be passed on to the callback method along with the response. (optional)| 459 | | priority | **number** | Priority in queue (1 is highest priority) (optional)| 460 | 461 | ### Example: 462 | ``` 463 | var ChessWebAPI = require('chess-web-api'); 464 | 465 | var chessAPI = new ChessWebAPI({ 466 | queue: true, 467 | }); 468 | 469 | let printResults = function(response, error, sampleParameter1, sampleParameter2) { 470 | console.log(response.body); 471 | console.log(sampleParameter1); 472 | console.log(sampleParameter2); 473 | } 474 | 475 | chessAPI.dispatch(chessAPI.getPlayer, printResults, ["andyruwruw"], {}, 1, ["callbackParameter", "anotherCallbackParameter"]); 476 | chessAPI.dispatch(chessAPI.getTitledPlayers, printResults, ["GM"]); 477 | chessAPI.dispatch(chessAPI.getPlayerCurrentDailyChess, printResults, ["andyruwruw"], ["callbackParameter"]); 478 | chessAPI.dispatch(chessAPI.getPlayerCompleteMonthlyArchives, printResults, ["andyruwruw", 2019, 10], {}, ["callback parameter"]); 479 | ``` 480 | 481 | If you initialize your **ChessWebAPI** with the queue enabled, you can still call any of the regular functions without using the queue. 482 | 483 | ``` 484 | var ChessWebAPI = require('chess-web-api'); 485 | 486 | var chessAPI = new ChessWebAPI({ 487 | queue: true, 488 | }); 489 | 490 | chessAPI.getTitledPlayers('GM') 491 | .then(function(response) { 492 | console.log('Grand Masters', response.body.players); 493 | }, function(err) { 494 | console.error(err); 495 | }); 496 | ``` 497 |

clearQueue()

498 | 499 | Should you wish to clear the queue you can run the following method. 500 | 501 | ### Example: 502 | 503 | ``` 504 | var ChessWebAPI = require('chess-web-api'); 505 | 506 | var chessAPI = new ChessWebAPI({ 507 | queue: true, 508 | }); 509 | 510 | let printResults = function(response, error) { 511 | console.log(response.body); 512 | } 513 | 514 | chessAPI.dispatch(chessAPI.getPlayer, printResults, ["andyruwruw"], {}, 1, ["callbackParameter", "anotherCallbackParameter"]); 515 | 516 | chessAPI.clearQueue(); 517 | ``` 518 | 519 |

Query for Changes

520 | 521 | The Chess.com public data API also allows clients to provide an **etag** from a previous response to check if data since the last request has changed. 522 | 523 | This has been implemented into chess-web-api's **ifChanged** method. 524 | 525 |

526 | 527 | ifChanged(etag, method, parameters, options, callback) 528 | 529 |

530 | 531 | Allows you to make any of the helper functions with the added parameter of the **etag** provided in the header of the last simular request. 532 | 533 | ### Parameters 534 | | Name | Type | Description | 535 | |------------|------------|---------------------------------------------------| 536 | | etag | **string** | ID of last request made. Found in the header. | 537 | | method | **function** | **chess-web-api** function for request. | 538 | | parameters | **array** | Array of parameters to be passed into the method. | 539 | | options | **object** | Added options to the end of the URL (optional) | 540 | | callback | **function** | Function to be called with result (optional) | 541 | ### Returns 542 | ``` 543 | // If data has changed, response is attatched. 544 | { 545 | changed: true, 546 | response: // response from chess.com 547 | } 548 | 549 | // If data has not been updated. 550 | { 551 | changed: false, 552 | } 553 | ``` 554 | ### Example: 555 | ``` 556 | var games, etag; 557 | 558 | // Gets the games and stores an etag. 559 | var trackGames = async function() { 560 | let response = await chessAPI.getPlayerCurrentDailyChess('andyruwruw'); 561 | games = response.body.games; 562 | etag = response.headers.etag; 563 | 564 | // runs checkGames every 10 seconds. 565 | setInterval(checkGames, 10000); 566 | } 567 | 568 | var checkGames = async function() { 569 | // Makes the request but with ifChanged 570 | let gamesChanged = await chessAPI.ifChanged(etag, chessAPI.getPlayerCurrentDailyChess, ['andyruwruw']); 571 | 572 | // Updates variables if new data is available. 573 | if (gamesChanged.changed) { 574 | games = gamesChanged.response.body.games; 575 | etag = gamesChanged.response.headers.etag; 576 | } 577 | } 578 | 579 | trackGames(); 580 | ``` 581 | --- 582 | ## ENJOY THE WRAPPER 583 | ![alt text](https://i.pinimg.com/originals/f6/34/96/f6349647c1537e61dd2f5df68a7b7a92.gif) 584 | ### - Andrew Young 585 | -------------------------------------------------------------------------------- /types/main.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Helpers { 2 | type ResponseData = Promise<{ 3 | body: T; 4 | headers?: U; 5 | statusCode?: number; 6 | }>; 7 | 8 | type RequestFunctionDefaultParams = [options?: object, callback?: () => any]; 9 | 10 | type RequestFunction< 11 | T extends readonly any[] = any[], 12 | U = object, 13 | R = object 14 | > = (...args: T) => Promise>; 15 | } 16 | 17 | declare module "chess-web-api" { 18 | export type NormalChessStats = { 19 | last: { rating: number; date: number; rd: number }; 20 | best: { 21 | rating: number; 22 | date: number; 23 | game: string; 24 | }; 25 | record: { 26 | win: number; 27 | loss: number; 28 | draw: number; 29 | }; 30 | }; 31 | 32 | export type PlayerColor = "black" | "white"; 33 | 34 | export type GameResultCode = 35 | | "win" 36 | | "checkmated" 37 | | "agreed" 38 | | "repetition" 39 | | "timeout" 40 | | "resigned" 41 | | "stalemate" 42 | | "lose" 43 | | "insufficient" 44 | | "50move" 45 | | "abandoned" 46 | | "kingofthehill" 47 | | "threecheck" 48 | | "timevsinsufficient" 49 | | "bughousepartnerlose"; 50 | 51 | export type MatchPlayer = { 52 | username: string; 53 | rating: 1492; 54 | result: GameResultCode; 55 | "@id": string; 56 | }; 57 | 58 | export type Match = { 59 | name: string; 60 | url: string; 61 | "@id": string; 62 | club: string; 63 | }; 64 | 65 | export type MatchInProgress = Match & { 66 | board: string; 67 | }; 68 | 69 | export type FinishedMatch = MatchInProgress & { 70 | results: { 71 | played_as_white: GameResultCode; 72 | played_as_black: GameResultCode; 73 | }; 74 | }; 75 | 76 | export type PlayerTournamentStatus = 77 | | "winner" 78 | | "eliminated" 79 | | "withdrew" 80 | | "removed"; 81 | 82 | export type PlayerTournament = { 83 | url: string; 84 | "@id": string; 85 | status: PlayerTournamentStatus; 86 | }; 87 | 88 | export type FinishedPlayerTournament = PlayerTournament & { 89 | wins: number; 90 | losses: number; 91 | draws: number; 92 | points_awarded: number; 93 | placement: number; 94 | }; 95 | 96 | export type ClubMatch = { 97 | name: string; 98 | "@id": string; 99 | opponent: string; 100 | time_class: string; 101 | }; 102 | 103 | export type ClubMatchInProgress = ClubMatch & { 104 | start_time: number; 105 | }; 106 | 107 | export type FinishedClubMatch = ClubMatchInProgress & { 108 | result: GameResultCode; 109 | }; 110 | 111 | export type TeamMatchTeam = { 112 | "@id": string; 113 | url: string; 114 | name: string; 115 | score: number; 116 | result: string; 117 | players: { 118 | username: string; 119 | stats: string; 120 | status: "basic" | "premium"; 121 | played_as_white: GameResultCode; 122 | played_as_black: GameResultCode; 123 | board: string; 124 | }[]; 125 | }; 126 | 127 | export type LeaderboardPlayer = { 128 | player_id: number; 129 | "@id": string; 130 | url: string; 131 | username: string; 132 | score: number; 133 | rank: number; 134 | title?: ChessTitle; 135 | name?: string; 136 | trend_score: { direction: number; delta: number }; 137 | trend_rank: { direction: number; delta: number }; 138 | status: string; 139 | avatar: string; 140 | flair_code: string; 141 | win_count: number; 142 | loss_count: number; 143 | draw_count: number; 144 | country: string; 145 | }; 146 | 147 | export type ChessTitle = 148 | | "GM" 149 | | "WGM" 150 | | "IM" 151 | | "WIM" 152 | | "FM" 153 | | "WFM" 154 | | "NM" 155 | | "WNM" 156 | | "CM" 157 | | "WCM"; 158 | 159 | export default class { 160 | constructor(options?: { queue: boolean }); 161 | 162 | /** 163 | * Get additional details about a player in a game. 164 | * @param {string} username - Username of desired profile. 165 | * @param {object} [options] - Added options to the end of the URL (optional). 166 | * @param {function} [callback] - Function to be called with result (optional). 167 | */ 168 | getPlayer: Helpers.RequestFunction< 169 | [username: string, ...args: Helpers.RequestFunctionDefaultParams], 170 | { 171 | "@id": string; 172 | url: string; 173 | username: string; 174 | player_id: number; 175 | title?: ChessTitle; 176 | status: string; 177 | name?: string; 178 | avatar?: string; 179 | location?: string; 180 | country: string; 181 | joined: number; 182 | last_online: number; 183 | followers: number; 184 | is_streamer: boolean; 185 | twitch_url: string; 186 | fide: number; 187 | } 188 | >; 189 | 190 | /** 191 | * Get ratings, win/loss, and other stats about a player's game play, tactics, lessons and Puzzle Rush score. 192 | * @param {string} username - Username of desired profile. 193 | * @param {object} [options] - Added options to the end of the URL (optional). 194 | * @param {function} [callback] - Function to be called with result (optional). 195 | */ 196 | getPlayerStats: Helpers.RequestFunction< 197 | [username: string, ...args: Helpers.RequestFunctionDefaultParams], 198 | { 199 | chess_daily: { 200 | last: { rating: number; date: number; rd: number }; 201 | record: { 202 | win: number; 203 | loss: number; 204 | draw: number; 205 | time_per_move: number; 206 | timeout_percent: number; 207 | }; 208 | }; 209 | chess_rapid: NormalChessStats; 210 | chess_bullet: NormalChessStats; 211 | chess_blitz: NormalChessStats; 212 | fide: number; 213 | tactics: { 214 | highest: { rating: number; date: number }; 215 | lowest: { rating: number; date: number }; 216 | }; 217 | puzzle_rush: { 218 | best: { 219 | total_attempts: number; 220 | score: number; 221 | }; 222 | }; 223 | } 224 | >; 225 | 226 | /** 227 | * @deprecated This endpoint was removed by Chess.com 8/25/2021. For more information see the forum post. This endpoint requires a large number of internal resources to maintain, and in the past week it has been used by only one clear developer script with a user-agent that helps us contact the developer. All other access to that endpoint appears to be tests, runaway scripts, or unidentified programs that could work better. 228 | * @param {string} username - Username of desired profile. 229 | * @param {object} [options] - Added options to the end of the URL (optional). 230 | * @param {function} [callback] - Function to be called with result (optional). 231 | */ 232 | getPlayerOnline: Helpers.RequestFunction< 233 | [username: string, ...args: Helpers.RequestFunctionDefaultParams], 234 | {} 235 | >; 236 | 237 | /** 238 | * Array of Daily Chess games that a player is currently playing. 239 | * @param {string} username - Username of desired profile. 240 | * @param {object} [options] - Added options to the end of the URL (optional). 241 | * @param {function} [callback] - Function to be called with result (optional). 242 | */ 243 | getPlayerCurrentDailyChess: Helpers.RequestFunction< 244 | [username: string, ...args: Helpers.RequestFunctionDefaultParams], 245 | { 246 | games: { 247 | white: string; 248 | black: string; 249 | url: string; 250 | fen: string; 251 | pgn: string; 252 | turn: PlayerColor; 253 | move_by: number; 254 | draw_offer?: PlayerColor; 255 | last_activity: number; 256 | start_time: number; 257 | time_control: string; 258 | time_class: string; 259 | rules: string; 260 | tournament?: string; 261 | match?: string; 262 | }[]; 263 | } 264 | >; 265 | 266 | /** 267 | * Array of Daily Chess games where it is the player's turn to act. 268 | * @param {string} username - Username of desired profile. 269 | * @param {object} [options] - Added options to the end of the URL (optional). 270 | * @param {function} [callback] - Function to be called with result (optional). 271 | */ 272 | getPlayerToMoveDailyChess: Helpers.RequestFunction< 273 | [username: string, ...args: Helpers.RequestFunctionDefaultParams], 274 | { 275 | games: { 276 | url: string; 277 | move_by: 1254438881; 278 | draw_offer?: true; 279 | last_activity: 1509810789; 280 | }[]; 281 | } 282 | >; 283 | 284 | /** 285 | * Array of monthly archives available for this player. 286 | * @param {string} username - Username of desired profile. 287 | * @param {object} [options] - Added options to the end of the URL (optional). 288 | * @param {function} [callback] - Function to be called with result (optional). 289 | */ 290 | getPlayerMonthlyArchives: Helpers.RequestFunction< 291 | [username: string, ...args: Helpers.RequestFunctionDefaultParams], 292 | { archives: string[] } 293 | >; 294 | 295 | /** 296 | * Array of Live and Daily Chess games that a player has finished. 297 | * @param {string} username - Username of desired profile. 298 | * @param {(string | number)} year - Year of matches. 299 | * @param {(string | number)} month - Month of matches. 300 | * @param {object} [options] - Added options to the end of the URL (optional). 301 | * @param {function} [callback] - Function to be called with result (optional). 302 | */ 303 | getPlayerCompleteMonthlyArchives: Helpers.RequestFunction< 304 | [ 305 | username: string, 306 | year: string | number, 307 | month: string | number, 308 | ...args: Helpers.RequestFunctionDefaultParams 309 | ], 310 | { 311 | archives: { 312 | white: MatchPlayer; 313 | black: MatchPlayer; 314 | accuracies: { 315 | white: number; 316 | black: number; 317 | }; 318 | url: string; 319 | fen: string; 320 | pgn: string; 321 | start_time: number; 322 | end_time: number; 323 | time_control: string; 324 | rules: string; 325 | eco?: string; 326 | tournament?: string; 327 | match?: string; 328 | }[]; 329 | } 330 | >; 331 | 332 | /** 333 | * Standard multi-game format PGN containing all games for a month. 334 | * @param {string} username - Username of desired profile. 335 | * @param {(string | number)} year - Year of matches. 336 | * @param {(string | number)} month - Month of matches. 337 | * @param {object} [options] - Added options to the end of the URL (optional). 338 | * @param {function} [callback] - Function to be called with result (optional). 339 | */ 340 | getPlayerMultiGamePGN: Helpers.RequestFunction< 341 | [ 342 | username: string, 343 | year: string | number, 344 | month: string | number, 345 | ...args: Helpers.RequestFunctionDefaultParams 346 | ], 347 | Buffer 348 | >; 349 | 350 | /** 351 | * List of clubs the player is a member of, with joined date and last activity date. 352 | * @param {string} username - Username of desired profile. 353 | * @param {object} [options] - Added options to the end of the URL (optional). 354 | * @param {function} [callback] - Function to be called with result (optional). 355 | */ 356 | getPlayerClubs: Helpers.RequestFunction< 357 | [username: string, ...args: Helpers.RequestFunctionDefaultParams], 358 | { 359 | clubs: { 360 | "@id": string; 361 | name: string; 362 | last_activity: number; 363 | icon: string; 364 | url: number; 365 | joined: number; 366 | }[]; 367 | } 368 | >; 369 | 370 | /** 371 | * List of Team matches the player has attended, is partecipating or is currently registered. 372 | * @param {string} username - Username of desired profile. 373 | * @param {object} [options] - Added options to the end of the URL (optional). 374 | * @param {function} [callback] - Function to be called with result (optional). 375 | */ 376 | getPlayerMatches: Helpers.RequestFunction< 377 | [username: string, ...args: Helpers.RequestFunctionDefaultParams], 378 | { 379 | finished: FinishedMatch[]; 380 | in_progress: MatchInProgress[]; 381 | registered: Match[]; 382 | } 383 | >; 384 | 385 | /** 386 | * List of tournaments the player is registered, is attending or has attended in the past. 387 | * @param {string} username - Username of desired profile. 388 | * @param {object} [options] - Added options to the end of the URL (optional). 389 | * @param {function} [callback] - Function to be called with result (optional). 390 | */ 391 | getPlayerTournaments: Helpers.RequestFunction< 392 | [username: string, ...args: Helpers.RequestFunctionDefaultParams], 393 | { 394 | finished: FinishedPlayerTournament[]; 395 | in_progress: PlayerTournament[]; 396 | registered: PlayerTournament[]; 397 | } 398 | >; 399 | 400 | /** 401 | * Game data lookup by ID. Includes PGN, metadata, and player info. For more information on what fields this generates or how it was implemented, 402 | * {@link https://github.com/andyruwruw/chess-web-api/blob/master/documentation/GAME.md}. 403 | * 404 | * Read Before Using: Fair Usage 405 | * 406 | * chess-web-api's getGameById method is not an official endpoint of Chess.com's Published Data API. It uses a callback from Chess.com's website to get its data Therefore it is highly unstable and could be changed without warning or stop functioning. Hammering this endpoint with requests could result in an IP ban from Chess.com. This would not only impact your script, but your access to Chess.com itself. The use of this endpoint violates Chess.com's Terms of Service, however Chess.com has informed me that as long as the "script is polite", they have no issue and it will go unnoticed. Corrective action will be taken if there is abuse. There is work to add a simular endpoint to Chess.com, and this library will be updated when that takes place. There is no current timeline there and it could take up to a year. 407 | * Please submit an issue if you notice any strange behavior to improve this documentation or make fixes. I'll periodically check to make sure the endpoint is working as intended and make fixes when needed. 408 | * 409 | * @param {string} id - The game ID. 410 | * @param {object} [options] - Added options to the end of the URL (optional). 411 | * @param {function} [callback] - Function to be called with result (optional). 412 | */ 413 | getGameByID: Helpers.RequestFunction< 414 | [id: string, ...args: Helpers.RequestFunctionDefaultParams], 415 | { 416 | game: { 417 | allowVacation: boolean; 418 | baseTime1: number; 419 | canSendTrophy: boolean; 420 | changesPlayersRating: number; 421 | colorOfWinner: string; 422 | endTime: number; 423 | id: number; 424 | initialSetup: string; 425 | isLiveGame: boolean; 426 | isAbortable: boolean; 427 | isAnalyzable: boolean; 428 | isCheckmate: boolean; 429 | isStalemate: boolean; 430 | isFinished: boolean; 431 | isRated: boolean; 432 | isResignable: boolean; 433 | lastMove: string; 434 | moveList: string; 435 | moveTimestamps: string; 436 | pgn: string; 437 | pgnHeaders: { 438 | Black: string; 439 | BlackElo: number; 440 | Date: string; 441 | ECO: string; 442 | Event: string; 443 | FEN: string; 444 | Result: string; 445 | SetUp: string; 446 | Site: string; 447 | TimeControl: string; 448 | White: string; 449 | WhiteElo: string; 450 | }; 451 | plyCount: number; 452 | ratingChangeWhite: number; 453 | ratingChangeBlack: number; 454 | resultMessage: string; 455 | timeIncrement1: 0; 456 | turnColor: string; 457 | type: string; 458 | typeName: string; 459 | }; 460 | players: { 461 | top: { 462 | avatarUrl: string; 463 | canWinOnTime: boolean; 464 | color: string; 465 | countryId: number; 466 | countryName: string; 467 | defaultTab: number; 468 | flairCode: string; 469 | gamesInProgress: number; 470 | hasMovedAtLeastOnce: boolean; 471 | id: number; 472 | isContentHidden: boolean; 473 | isDrawable: boolean; 474 | isEnabled: boolean; 475 | isInLivechess: boolean; 476 | isOnline: boolean; 477 | isTouchMove: boolean; 478 | isVacation: boolean; 479 | isWhiteOnBottom: boolean; 480 | lastLoginDate: number; 481 | location: string; 482 | memberSince: number; 483 | membershipCode: string; 484 | membershipLevel: number; 485 | offeredDraw: boolean; 486 | postMoveAction: string; 487 | rating: number; 488 | turnTimeRemaining: string; 489 | username: string; 490 | vacationRemaining: string; 491 | }; 492 | bottom: { 493 | avatarUrl: string; 494 | canWinOnTime: boolean; 495 | color: string; 496 | countryId: number; 497 | countryName: string; 498 | defaultTab: number; 499 | flairCode: string; 500 | gamesInProgress: number; 501 | hasMovedAtLeastOnce: boolean; 502 | id: number; 503 | isContentHidden: boolean; 504 | isDrawable: boolean; 505 | isEnabled: boolean; 506 | isInLivechess: boolean; 507 | isOnline: boolean; 508 | isTouchMove: boolean; 509 | isVacation: boolean; 510 | isWhiteOnBottom: boolean; 511 | lastLoginDate: number; 512 | location: string; 513 | memberSince: number; 514 | membershipCode: string; 515 | membershipLevel: number; 516 | offeredDraw: boolean; 517 | postMoveAction: string; 518 | rating: number; 519 | turnTimeRemaining: string; 520 | username: string; 521 | vacationRemaining: string; 522 | }; 523 | }; 524 | } 525 | >; 526 | 527 | /** 528 | * Get additional details about a club. 529 | * 530 | * All club-based URLs use the club's "URL ID" to specify which club you want data for. 531 | * 532 | * The url-ID is the same as found in the URL for the club's web page on www.chess.com. For example, the url-ID of the Chess.com Developer's Club is chess-com-developer-community. 533 | * 534 | * @param {string} urlID - Club's unique urlID. 535 | * @param {object} [options] - Added options to the end of the URL (optional). 536 | * @param {function} [callback] - Function to be called with result (optional). 537 | */ 538 | getClub: Helpers.RequestFunction< 539 | [urlID: string, ...args: Helpers.RequestFunctionDefaultParams], 540 | { 541 | "@id": string; 542 | name: string; 543 | club_id: number; 544 | icon: string; 545 | country: string; 546 | average_daily_rating: number; 547 | members_count: number; 548 | created: number; 549 | last_activity: number; 550 | visibility: "public" | "private"; 551 | join_request: string; 552 | admin: string[]; 553 | description: string; 554 | } 555 | >; 556 | 557 | /** 558 | * List of club members (usernames and joined date timestamp), grouped by club-activity frequency. The club-activity is one of this actions. 559 | * 560 | * @param {string} urlID - Club's unique urlID. 561 | * @param {object} [options] - Added options to the end of the URL (optional). 562 | * @param {function} [callback] - Function to be called with result (optional). 563 | */ 564 | getClubMembers: Helpers.RequestFunction< 565 | [urlID: string, ...args: Helpers.RequestFunctionDefaultParams], 566 | { 567 | username: string; 568 | joined: number; 569 | }[] 570 | >; 571 | 572 | /** 573 | * List of daily and club matches, grouped by status (registered, in progress, finished). 574 | * 575 | * @param {string} urlID - Club's unique urlID. 576 | * @param {object} [options] - Added options to the end of the URL (optional). 577 | * @param {function} [callback] - Function to be called with result (optional). 578 | */ 579 | getClubMatches: Helpers.RequestFunction< 580 | [urlID: string, ...args: Helpers.RequestFunctionDefaultParams], 581 | { 582 | finished: FinishedClubMatch[]; 583 | in_progress: ClubMatchInProgress[]; 584 | registered: ClubMatch[]; 585 | } 586 | >; 587 | 588 | /** 589 | * Get details about a daily, live and arena tournament. 590 | * 591 | * All tournaments-based URLs use the tournament's "URL ID" to specify which tournament you want data for. 592 | * 593 | * The url-ID is the same as found in the URL for the tournament's web page on www.chess.com. For example, the url-ID of the Chess.com Developer's Club is 594 | * -33rd-chesscom-quick-knockouts-1401-1600 595 | * 596 | * @param {string} urlID - Tournaments's unique urlID 597 | * @param {object} [options] - Added options to the end of the URL (optional). 598 | * @param {function} [callback] - Function to be called with result (optional). 599 | */ 600 | getTournament: Helpers.RequestFunction< 601 | [urlID: string, ...args: Helpers.RequestFunctionDefaultParams], 602 | { 603 | name: string; 604 | url: string; 605 | description: string; 606 | creator: string; 607 | status: string; 608 | finish_time: number; 609 | settings: { 610 | type: string; 611 | rules: string; 612 | time_class: string; 613 | time_control: string; 614 | is_rated: true; 615 | is_official: false; 616 | is_invite_only: false; 617 | initial_group_size: number; 618 | user_advance_count: number; 619 | use_tiebreak: true; 620 | allow_vacation: false; 621 | winner_places: number; 622 | registered_user_count: number; 623 | games_per_opponent: number; 624 | total_rounds: number; 625 | concurrent_games_per_opponent: number; 626 | }; 627 | players: { 628 | username: string; 629 | status: string; 630 | }[]; 631 | rounds: string[]; 632 | } 633 | >; 634 | 635 | /** 636 | * Get details about a tournament's round. 637 | * 638 | * @param {string} urlID - Tournaments's unique urlID 639 | * @param {(string | number)} round - Round number 640 | * @param {object} [options] - Added options to the end of the URL (optional). 641 | * @param {function} [callback] - Function to be called with result (optional). 642 | */ 643 | getTournamentRound: Helpers.RequestFunction< 644 | [ 645 | urlID: string, 646 | round: string | number, 647 | ...args: Helpers.RequestFunctionDefaultParams 648 | ], 649 | { 650 | groups: string[]; 651 | players: { 652 | username: string; 653 | is_advancing?: boolean; 654 | }[]; 655 | } 656 | >; 657 | 658 | /** 659 | * Get details about a tournament's round group. 660 | * 661 | * @param {string} urlID - Tournaments's unique urlID 662 | * @param {(string | number)} round - Round number 663 | * @param {(string | number)} group - Group number 664 | * @param {object} [options] - Added options to the end of the URL (optional). 665 | * @param {function} [callback] - Function to be called with result (optional). 666 | */ 667 | getTournamentRoundGroup: Helpers.RequestFunction< 668 | [ 669 | urlID: string, 670 | round: string | number, 671 | group: string | number, 672 | ...args: Helpers.RequestFunctionDefaultParams 673 | ], 674 | { 675 | fair_play_removals: string[]; 676 | games: { 677 | white: string; 678 | black: string; 679 | url: string; 680 | fen: string; 681 | pgn: string; 682 | turn: PlayerColor; 683 | move_by: number; 684 | draw_offer?: PlayerColor; 685 | last_activity: number; 686 | start_time: number; 687 | time_control: string; 688 | time_class: string; 689 | rules: string; 690 | eco: string; 691 | tournament: string; 692 | }[]; 693 | players: { 694 | username: string; 695 | points: number; 696 | tie_break: number; 697 | id_advancing: boolean; 698 | }[]; 699 | } 700 | >; 701 | 702 | /** 703 | * Get details about a team match and players playing that match. After the match is finished there will be a link to each player's stats endpoint, in order to get up-to-date information about the player. 704 | * 705 | * All team matches-based URLs use the match "ID" to specify which match you want data for. 706 | * 707 | * The ID is the same as found in the URL for the team match web page on www.chess.com. For example, the ID WORLD LEAGUE Round 5: Romania vs USA Southwest is 12803. 708 | * 709 | * @param {(string | number)} id - Id of desired team match. 710 | * @param {object} [options] - Added options to the end of the URL (optional). 711 | * @param {function} [callback] - Function to be called with result (optional). 712 | */ 713 | getTeamMatch: Helpers.RequestFunction< 714 | [id: string | number, ...args: Helpers.RequestFunctionDefaultParams], 715 | { 716 | name: string; 717 | url: string; 718 | description: string; 719 | start_time: number; 720 | settings: { 721 | time_class: string; 722 | time_control: string; 723 | initial_setup: string; 724 | rules: string; 725 | min_team_players: number; 726 | max_team_players: number; 727 | min_required_games: number; 728 | min_rating: number; 729 | max_rating: number; 730 | autostart: boolean; 731 | }; 732 | status: string; 733 | boards: number; 734 | teams: { 735 | team1: TeamMatchTeam; 736 | team2: TeamMatchTeam; 737 | }; 738 | } 739 | >; 740 | 741 | /** 742 | * Get details about a team match board. Only in-progress or finished games will be included, so there may be one or two games in this list. 743 | * 744 | * @param {(string | number)} id - Id of desired team match. 745 | * @param {(string | number)} board - Board identifier. 746 | * @param {object} [options] - Added options to the end of the URL (optional). 747 | * @param {function} [callback] - Function to be called with result (optional). 748 | */ 749 | getTeamMatchBoard: Helpers.RequestFunction< 750 | [ 751 | id: string | number, 752 | board: string | number, 753 | ...args: Helpers.RequestFunctionDefaultParams 754 | ], 755 | { 756 | board_scores: { 757 | player1: number; 758 | player2: number; 759 | }; 760 | games: { 761 | white: MatchPlayer & { team: string }; 762 | black: MatchPlayer & { team: string }; 763 | accuracies?: { 764 | white: number; 765 | black: number; 766 | }; 767 | url: string; 768 | fen: string; 769 | pgn: string; 770 | start_time: number; 771 | end_time: number; 772 | time_control: string; 773 | time_class: string; 774 | rules: string; 775 | eco: string; 776 | match: string; 777 | }[]; 778 | } 779 | >; 780 | 781 | /** 782 | * Get details about a team match and players playing that match. After the match is finished there will be a link to each player's stats endpoint, in order to get up-to-date information about the player. 783 | * 784 | * All live team matches-based URLs use the match "ID" to specify which match you want data for. 785 | * 786 | * The ID is the same as found in the URL for the team match web page on www.chess.com. For example, the ID Friendly 5+2 is 5833. 787 | * 788 | * @param {(string | number)} id - Id of desired live team match. 789 | * @param {object} [options] - Added options to the end of the URL (optional). 790 | * @param {function} [callback] - Function to be called with result (optional). 791 | */ 792 | getTeamLiveMatch: Helpers.RequestFunction< 793 | [id: string | number, ...args: Helpers.RequestFunctionDefaultParams], 794 | { 795 | "@id": string; 796 | name: string; 797 | url: string; 798 | start_time: number; 799 | status: string; 800 | boards: number; 801 | settings: { 802 | rules: string; 803 | time_class: string; 804 | time_control: number; 805 | time_increment: number; 806 | min_team_players: number; 807 | min_required_games: number; 808 | autostart: false; 809 | }; 810 | teams: { 811 | team1: TeamMatchTeam; 812 | team2: TeamMatchTeam; 813 | }; 814 | } 815 | >; 816 | 817 | /** 818 | * Get details about a team match board. Only in-progress or finished games will be included, so there may be one or two games in this list. 819 | * 820 | * @param {(string | number)} id - Id of desired live team match.. 821 | * @param {(string | number)} board - Board identifier. 822 | * @param {object} [options] - Added options to the end of the URL (optional). 823 | * @param {function} [callback] - Function to be called with result (optional). 824 | */ 825 | getTeamLiveMatchBoard: Helpers.RequestFunction< 826 | [ 827 | id: string | number, 828 | board: string | number, 829 | ...args: Helpers.RequestFunctionDefaultParams 830 | ], 831 | { 832 | board_scores: { 833 | [player: string]: number; 834 | }; 835 | games: { 836 | white: MatchPlayer; 837 | black: MatchPlayer; 838 | url: string; 839 | fen: string; 840 | pgn: string; 841 | start_time: number; 842 | end_time: number; 843 | time_control: string; 844 | time_class: string; 845 | rules: string; 846 | eco: string; 847 | rated: boolean; 848 | }[]; 849 | } 850 | >; 851 | 852 | /** 853 | * Get additional details about a country. 854 | * 855 | * All country-based URLs use the country's 2-character ISO 3166 code (capitalized) to specify which country you want data for. 856 | * 857 | * Find their code's here. 858 | * 859 | * {@link https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2} 860 | * 861 | * Additional countries not listed on that official list have been posted by Chess.com. 862 | * 863 | * @param {string} iso - Country's ISO identifier 864 | * @param {object} [options] - Added options to the end of the URL (optional). 865 | * @param {function} [callback] - Function to be called with result (optional). 866 | */ 867 | getCountry: Helpers.RequestFunction< 868 | [iso: string, ...args: Helpers.RequestFunctionDefaultParams], 869 | { 870 | "@id": string; 871 | name: string; 872 | code: string; 873 | } 874 | >; 875 | 876 | /** 877 | * List of usernames for players who identify themselves as being in this country. 878 | * 879 | * @param {string} iso - Country's ISO identifier 880 | * @param {object} [options] - Added options to the end of the URL (optional). 881 | * @param {function} [callback] - Function to be called with result (optional). 882 | */ 883 | getCountryPlayers: Helpers.RequestFunction< 884 | [iso: string, ...args: Helpers.RequestFunctionDefaultParams], 885 | { 886 | players: string[]; 887 | } 888 | >; 889 | 890 | /** 891 | * List of URLs for clubs identified as being in or associated with this country. 892 | * 893 | * @param {string} iso - Country's ISO identifier 894 | * @param {object} [options] - Added options to the end of the URL (optional). 895 | * @param {function} [callback] - Function to be called with result (optional). 896 | */ 897 | getCountryClubs: Helpers.RequestFunction< 898 | [iso: string, ...args: Helpers.RequestFunctionDefaultParams], 899 | { 900 | clubs: string[]; 901 | } 902 | >; 903 | 904 | /** 905 | * Information about the daily puzzle found in www.chess.com. 906 | * 907 | * @param {object} [options] - Added options to the end of the URL (optional). 908 | * @param {function} [callback] - Function to be called with result (optional). 909 | */ 910 | getDailyPuzzle: Helpers.RequestFunction< 911 | [...args: Helpers.RequestFunctionDefaultParams], 912 | { 913 | title: string; 914 | url: string; 915 | publish_time: number; 916 | fen: string; 917 | pgn: string; 918 | image: string; 919 | } 920 | >; 921 | 922 | /** 923 | * Information about a randomly picked daily puzzle. 924 | * 925 | * @param {object} [options] - Added options to the end of the URL (optional). 926 | * @param {function} [callback] - Function to be called with result (optional). 927 | */ 928 | getDailyPuzzleRandom: Helpers.RequestFunction< 929 | [...args: Helpers.RequestFunctionDefaultParams], 930 | { 931 | title: string; 932 | url: string; 933 | publish_time: number; 934 | fen: string; 935 | pgn: string; 936 | image: string; 937 | } 938 | >; 939 | 940 | /** 941 | * Information about Chess.com streamers. 942 | * 943 | * @param {object} [options] - Added options to the end of the URL (optional). 944 | * @param {function} [callback] - Function to be called with result (optional). 945 | */ 946 | getStreamers: Helpers.RequestFunction< 947 | [...args: Helpers.RequestFunctionDefaultParams], 948 | { 949 | streamers: { 950 | username: string; 951 | avatar: string; 952 | twitch_url: string; 953 | url: string; 954 | }[]; 955 | } 956 | >; 957 | 958 | /** 959 | * Information about Chess.com streamers. 960 | * 961 | * @param {object} [options] - Added options to the end of the URL (optional). 962 | * @param {function} [callback] - Function to be called with result (optional). 963 | */ 964 | getLeaderboards: Helpers.RequestFunction< 965 | [...args: Helpers.RequestFunctionDefaultParams], 966 | { 967 | daily: LeaderboardPlayer[]; 968 | daily960: LeaderboardPlayer[]; 969 | live_rapid: LeaderboardPlayer[]; 970 | live_blitz: LeaderboardPlayer[]; 971 | live_bullet: LeaderboardPlayer[]; 972 | live_bughouse: LeaderboardPlayer[]; 973 | live_blitz960: LeaderboardPlayer[]; 974 | live_threecheck: LeaderboardPlayer[]; 975 | live_crazyhouse: LeaderboardPlayer[]; 976 | live_kingofthehill: LeaderboardPlayer[]; 977 | lessons: LeaderboardPlayer[]; 978 | tactics: LeaderboardPlayer[]; 979 | } 980 | >; 981 | 982 | /** 983 | * Retrieves an array of usernames of players with a given title. 984 | * 985 | * Valid title abbreviations are: GM, WGM, IM, WIM, FM, WFM, NM, WNM, CM, WCM. 986 | * 987 | * @param {ChessTitle} titleAbbrev - GM, WGM, IM, WIM, FM, WFM, NM, WNM, CM, WCM. 988 | * @param {object} [options] - Added options to the end of the URL (optional). 989 | * @param {Function} [callback] - Function to be called with result (optional). 990 | */ 991 | getTitledPlayers: Helpers.RequestFunction< 992 | [titleAbbrev: ChessTitle, ...args: Helpers.RequestFunctionDefaultParams], 993 | { 994 | players: string[]; 995 | } 996 | >; 997 | 998 | /** 999 | * Adds an item to the priority queue. 1000 | * 1001 | * The provided callback method will be provided two parameters (response, error). Response is the full request result. 1002 | * 1003 | * Additional parameters can be passed to the callback in callbackParameters. 1004 | * 1005 | * The dispatch function determines the difference between parameters to be passed into the chess-web-api method vs the callback function by order. 1006 | * 1007 | * The first array passed into dispatch will always be passed into the chess-web-api method. 1008 | * 1009 | * The second array will always be sent to the callback function, with the response to the request as the first parameter. 1010 | * 1011 | * @param {Function} method - chess-web-api function for request. 1012 | * @param {Function} callback - Function to be called with result 1013 | * @param {any[]} parameters - Array of parameters to be passed into the method. 1014 | * @param {object} [options] - Added options to the end of the URL (optional) 1015 | * @param {any[]} [callbackParameters] - Array of parameters to be passed on to the callback method along with the response. (optional) 1016 | * @param {number} [priority] - Priority in queue (1 is highest priority) (optional) 1017 | */ 1018 | dispatch: ( 1019 | method: Helpers.RequestFunction, 1020 | callback: (response: any, error: any, ...params: any[]) => void, 1021 | parameters: any[], 1022 | options?: object, 1023 | callbackParameters?: any[], 1024 | priority?: number 1025 | ) => undefined; 1026 | 1027 | /** 1028 | * Should you wish to clear the queue you can run the following method. 1029 | */ 1030 | clearQueue: () => undefined; 1031 | 1032 | /** 1033 | * Allows you to make any of the helper functions with the added parameter of the etag provided in the header of the last simular request. 1034 | * 1035 | * @param {string} etag - ID of last request made. Found in the header. 1036 | * @param {Function} method - chess-web-api function for request. 1037 | * @param {any[]} parameters - Array of parameters to be passed into the method. 1038 | * @param {object} [options] - Added options to the end of the URL (optional). 1039 | * @param {function} [callback] - Function to be called with result (optional). 1040 | */ 1041 | ifChanged: ( 1042 | etag: string, 1043 | method: T, 1044 | parameters: Parameters, 1045 | ...args: Helpers.RequestFunctionDefaultParams 1046 | ) => Promise<{ 1047 | changed: boolean; 1048 | response: Awaited>; 1049 | }>; 1050 | } 1051 | } 1052 | --------------------------------------------------------------------------------