mtg-playerinfo 1.1.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,11 +7,12 @@ on:
7
7
  branches: [ main ]
8
8
 
9
9
  jobs:
10
- build:
11
- runs-on: ubuntu-latest
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
12
 
13
13
  strategy:
14
14
  matrix:
15
+ os: [ubuntu-latest, macos-latest, windows-latest]
15
16
  node-version: [20, 22, 24]
16
17
 
17
18
  steps:
@@ -27,4 +28,29 @@ jobs:
27
28
  run: npm install
28
29
 
29
30
  - name: Run tests
30
- run: npm test
31
+ run: npm run test:coverage
32
+
33
+ - name: Upload test coverage data
34
+ if: github.repository == 'bkimminich/mtg-playerinfo' && github.event_name == 'push' && matrix.os == 'ubuntu-latest' && matrix.node-version == '24'
35
+ uses: actions/upload-artifact@v6
36
+ with:
37
+ name: test-lcov
38
+ path: |
39
+ coverage/lcov.info
40
+
41
+ coverage-report:
42
+ needs: [test]
43
+ runs-on: ubuntu-latest
44
+ if: github.repository == 'bkimminich/mtg-playerinfo' && github.event_name == 'push'
45
+ steps:
46
+ - name: "Check out Git repository"
47
+ uses: actions/checkout@v4
48
+ - name: "Download test coverage data"
49
+ uses: actions/download-artifact@v7
50
+ with:
51
+ name: test-lcov
52
+ - name: "Publish coverage to Coveralls"
53
+ uses: coverallsapp/github-action@v2
54
+ with:
55
+ github-token: ${{ secrets.GITHUB_TOKEN }}
56
+ files: lcov.info
@@ -0,0 +1,33 @@
1
+ name: Update Test Data
2
+
3
+ on:
4
+ schedule:
5
+ - cron: '0 2 * * 0' # Sundays 2:00 AM
6
+ workflow_dispatch: # Allow manual run
7
+
8
+ jobs:
9
+ update:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+ steps:
14
+ - name: Checkout repository
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Use Node.js
18
+ uses: actions/setup-node@v4
19
+ with:
20
+ node-version: 24
21
+ cache: 'npm'
22
+
23
+ - name: Install dependencies
24
+ run: npm install
25
+
26
+ - name: Fetch new test data
27
+ run: node scripts/update-test-data.js
28
+
29
+ - name: Commit and push changes
30
+ uses: stefanzweifel/git-auto-commit-action@v5
31
+ with:
32
+ commit_message: "🤖 Update test data HTML files"
33
+ file_pattern: 'test/data/*.html'
package/README.md CHANGED
@@ -1,84 +1,109 @@
1
1
  # MTG Player Info
2
2
 
3
3
  ![GitHub stars](https://img.shields.io/github/stars/bkimminich/mtg-playerinfo.svg?label=GitHub%20%E2%98%85&style=flat)
4
- ![node](https://img.shields.io/node/v/mtg-playerinfo.svg) [![npm](https://img.shields.io/npm/dm/mtg-playerinfo.svg)](https://www.npmjs.com/package/mtg-playerinfo) [![npm](https://img.shields.io/npm/dt/mtg-playerinfo.svg)](https://www.npmjs.com/package/mtg-playerinfo)
4
+ ![node](https://img.shields.io/node/v/mtg-playerinfo.svg)
5
+ [![npm](https://img.shields.io/npm/dm/mtg-playerinfo.svg)](https://www.npmjs.com/package/mtg-playerinfo)
6
+ [![npm](https://img.shields.io/npm/dt/mtg-playerinfo.svg)](https://www.npmjs.com/package/mtg-playerinfo)
7
+ [![Coverage Status](https://coveralls.io/repos/github/bkimminich/mtg-playerinfo/badge.svg?branch=main)](https://coveralls.io/github/bkimminich/mtg-playerinfo?branch=main)
8
+ [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
5
9
 
6
10
  A simple NPM module and CLI tool to pull Magic: The Gathering player data from various sources (Unity League, MTG Elo Project, Melee, and Topdeck).
7
11
 
8
12
  ## Installation
9
13
 
10
14
  ```bash
11
- npm i -g mtg-player-info
15
+ npm i -g mtg-playerinfo
12
16
  ```
13
17
 
14
18
  ## CLI Usage
15
19
 
16
20
  ```bash
17
- mtg-player-info --unity-id 16215 --mtgelo-id 3irvwtmk --melee-user k0shiii --topdeck-handle k0shiii
21
+ mtg-playerinfo --unity-id 16215 --mtgelo-id 3irvwtmk --melee-user k0shiii --topdeck-handle k0shiii
18
22
  ```
19
23
 
20
24
  or without previous installation
21
25
 
22
26
  ```bash
23
- npx mtg-player-info --unity-id 16215 --mtgelo-id 3irvwtmk --melee-user k0shiii --topdeck-handle k0shiii
27
+ npx mtg-playerinfo --unity-id 16215 --mtgelo-id 3irvwtmk --melee-user k0shiii --topdeck-handle k0shiii
24
28
  ```
25
29
 
26
30
  ## Output Format
27
31
 
28
- The tool returns a JSON object representing the player and their combined metadata. Redundant information like Name, Photo, Country, Age, and Hometown is merged into a `general` section, while source-specific data is kept in the `sources` section.
32
+ The tool returns a JSON object representing the player and their combined metadata. Redundant information like name, photo, country, age, and hometown is merged into a `general` section, while source-specific data is kept in the `sources` section.
29
33
 
30
- ### Deduplication and Merging Logic
34
+ ### General meta-data and merging priority
31
35
 
32
- - **Priority**: Merging follows a "first-come, first-served" approach based on the order of sources provided in the command line or processed by the manager. For instance, the first `name` and first valid `photo` URL found will be used as the primary name and photo for the player.
33
- - **Deduplication**: If multiple IDs point to the exact same profile URL, the profile is only processed once to avoid redundant data in the `sources` section.
34
- - **General Metadata**: Fields like `Age`, `Country`, and `Hometown` are extracted from the first source that provides them and placed in the `general` section.
36
+ General meta-data fields like `name`, `photo`, `age`, `country`, and `hometown` are extracted from the first source that provides them and placed in the `general` section. Merging follows a "first-come, first-served" approach based on the order of sources provided in the command line or processed by the manager. In the [CLI usage example](#cli-usage) above, the source priority is `Unity League` > `MTG Elo Project` > `Melee` > `Topdeck`.
35
37
 
36
- Example output:
38
+ > If you notice any inconsistencies or unexpected fields values, you can run the tool with the `-v` or `--verbose` flag to see the full list of extracted fields and if they were promoted to the `general` section or deviated from a previous source.
39
+
40
+ ### Example output
37
41
 
38
42
  ```json
39
43
  {
40
- "name": "Björn Kimminich",
41
- "photo": "https://unityleague.gg/media/player_profile/1000023225.jpg",
42
44
  "general": {
43
- "Age": "45",
44
- "Bio": "Smugly held back on an Untimely Malfunction against a Storm player going off, being totally sure that you can redirect the summed-up damage of their Grapeshots back to their face with its \"Change the target of target spell or ability with a single target\" mode.",
45
- "Team": "Mull to Five",
46
- "Country": "de",
47
- "Hometown": "Hamburg",
48
- "Win Rate": "42.09%"
45
+ "name": "Björn Kimminich",
46
+ "photo": "https://unityleague.gg/media/player_profile/1000023225.jpg",
47
+ "age": "45",
48
+ "bio": "Smugly held back on an Untimely Malfunction against a Storm player going off, being totally sure that you can redirect the summed-up damage of their Grapeshots back to their face with its \"Change the target of target spell or ability with a single target\" mode.",
49
+ "team": "Mull to Five",
50
+ "country": "de",
51
+ "hometown": "Hamburg",
52
+ "pronouns": "He/Him",
53
+ "facebook": "bjoern.kimminich",
54
+ "twitch": "koshiii",
55
+ "youtube": "@BjörnKimminich",
56
+ "win rate": "42.49%"
49
57
  },
50
58
  "sources": {
51
59
  "Unity League": {
52
60
  "url": "https://unityleague.gg/player/16215/",
53
61
  "data": {
54
- "Local organizer": "Mulligan TCG Shop",
55
- "Rank Germany": "58",
56
- "Rank Europe": "584",
57
- "Rank Points": "274",
58
- "Record": "38-38-5",
59
- "Win Rate": "49.0%"
62
+ "name": "Björn Kimminich",
63
+ "photo": "https://unityleague.gg/media/player_profile/1000023225.jpg",
64
+ "country": "de",
65
+ "age": "45",
66
+ "hometown": "Hamburg",
67
+ "local organizer": "Mulligan TCG Shop",
68
+ "team": "Mull to Five",
69
+ "bio": "Smugly held back on an Untimely Malfunction against a Storm player going off, being totally sure that you can redirect the summed-up damage of their Grapeshots back to their face with its \"Change the target of target spell or ability with a single target\" mode.",
70
+ "rank germany": "63",
71
+ "rank europe": "547",
72
+ "rank points": "304",
73
+ "record": "45-43-5",
74
+ "win rate": "50.2%"
60
75
  }
61
76
  },
62
77
  "MTG Elo Project": {
63
78
  "url": "https://mtgeloproject.net/profile/3irvwtmk",
64
79
  "data": {
80
+ "name": "Bjoern Kimminich",
65
81
  "player_id": "3irvwtmk",
66
82
  "current_rating": "1466",
67
83
  "record": "9-12-1",
68
- "Win Rate": "40.91%"
84
+ "win rate": "40.91%"
69
85
  }
70
86
  },
71
87
  "Melee": {
72
88
  "url": "https://melee.gg/Profile/Index/k0shiii",
73
- "data": {}
89
+ "data": {
90
+ "name": "Björn Kimminich",
91
+ "pronouns": "He/Him",
92
+ "bio": "Smugly held back on an Untimely Malfunction against a Storm player going off, being totally sure that you can redirect the summed-up damage of their Grapeshots back to their face.",
93
+ "facebook": "bjoern.kimminich",
94
+ "twitch": "koshiii",
95
+ "youtube": "@BjörnKimminich"
96
+ }
74
97
  },
75
98
  "Topdeck": {
76
99
  "url": "https://topdeck.gg/profile/@k0shiii",
77
100
  "data": {
78
- "Tournaments": "2",
79
- "Record": "4-6-1",
80
- "Win Rate": "36.36%",
81
- "Conversion": "0%"
101
+ "name": "Björn Kimminich",
102
+ "photo": "https://imagedelivery.net/kN_u_RUfFF6xsGMKYWhO1g/2a7b8d12-5924-4a58-5f9c-c0bf55766800/square",
103
+ "tournaments": "2",
104
+ "record": "4-6-1",
105
+ "win rate": "36.36%",
106
+ "conversion": "0%"
82
107
  }
83
108
  }
84
109
  }
@@ -89,12 +114,12 @@ Example output:
89
114
 
90
115
  The following sites are currently supported based on HTML scraping and/or API calls. In general, API calls are preferred over scraping due to their higher reliability and independence from site structure changes.
91
116
 
92
- | Site | Method |
93
- |-----------------|------------------------------------------------------------------------------|
94
- | Unity League | ✅Scraping |
95
- | MTG Elo Project | ✅Scraping |
96
- | Topdeck | ✅Scraping |
97
- | Melee | 🚧Scraping/API ([#1](https://github.com/bkimminich/mtg-playerinfo/issues/1)) |
117
+ | Site | Method |
118
+ |-----------------|---------------------------------------------------------------------------------|
119
+ | Unity League | ✅Scraping |
120
+ | MTG Elo Project | ✅Scraping |
121
+ | Topdeck | ✅Scraping / ✅API |
122
+ | Melee | Scraping / 🚧API ([#1](https://github.com/bkimminich/mtg-playerinfo/issues/1)) |
98
123
 
99
124
  _Note: Some sites may have anti-bot protections that can lead to "Maximum number of redirects exceeded" or "403 Forbidden" errors depending on the execution environment._
100
125
 
package/cli.js CHANGED
@@ -1,31 +1,47 @@
1
1
  #!/usr/bin/env node
2
- const { program } = require('commander');
3
- const PlayerInfoManager = require('./src/index');
2
+ const { program } = require('commander')
3
+ const PlayerInfoManager = require('./src/index')
4
4
 
5
5
  program
6
6
  .name('mtg-playerinfo')
7
7
  .description('CLI to pull MTG player data from various sources')
8
- .version('1.0.0');
8
+ .version('1.0.0')
9
9
 
10
10
  program
11
11
  .option('--unity-id <id>', 'Unity League Player ID')
12
12
  .option('--mtgelo-id <id>', 'MTG Elo Project Player ID')
13
13
  .option('--melee-user <username>', 'Melee Username')
14
14
  .option('--topdeck-handle <handle>', 'Topdeck Handle')
15
+ .option('-v, --verbose', 'Print consistency check information to console')
15
16
  .action(async (options) => {
16
17
  if (!options.unityId && !options.mtgeloId && !options.meleeUser && !options.topdeckHandle) {
17
- console.error('Error: Please provide at least one search option (unity-id, mtgelo-id, melee-user, or topdeck-handle).');
18
- process.exit(1);
18
+ console.error('Error: Please provide at least one search option (unity-id, mtgelo-id, melee-user, or topdeck-handle).')
19
+ process.exit(1)
19
20
  }
20
21
 
21
- const manager = new PlayerInfoManager();
22
+ // Determine priority order based on CLI argument order
23
+ const argOrder = []
24
+ const optionMap = {
25
+ '--unity-id': 'unity',
26
+ '--mtgelo-id': 'mtgelo',
27
+ '--melee-user': 'melee',
28
+ '--topdeck-handle': 'topdeck'
29
+ }
30
+
31
+ process.argv.forEach(arg => {
32
+ if (optionMap[arg]) {
33
+ argOrder.push(optionMap[arg])
34
+ }
35
+ })
36
+
37
+ const manager = new PlayerInfoManager()
22
38
  try {
23
- const playerInfo = await manager.getPlayerInfo(options);
24
- console.log(JSON.stringify(playerInfo, null, 2));
39
+ const playerInfo = await manager.getPlayerInfo(options, argOrder)
40
+ console.log(JSON.stringify(playerInfo, null, 2))
25
41
  } catch (error) {
26
- console.error('An error occurred:', error.message);
27
- process.exit(1);
42
+ console.error('An error occurred:', error.message)
43
+ process.exit(1)
28
44
  }
29
- });
45
+ })
30
46
 
31
- program.parse(process.argv);
47
+ program.parse(process.argv)
package/package.json CHANGED
@@ -1,13 +1,28 @@
1
1
  {
2
2
  "name": "mtg-playerinfo",
3
- "version": "1.1.0",
3
+ "version": "1.2.2",
4
4
  "description": "A simple NPM module and CLI tool to pull Magic: The Gathering player data from various sources",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "mtg-playerinfo": "./cli.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "node --test"
10
+ "test": "node --test",
11
+ "test:coverage": "nyc --reporter=lcov --produce-source-map node --test",
12
+ "lint": "standard",
13
+ "lint:fix": "standard --fix"
14
+ },
15
+ "nyc": {
16
+ "extension": [
17
+ ".js"
18
+ ],
19
+ "include": [
20
+ "src/fetchers/*.js",
21
+ "src/index.js",
22
+ "cli.js"
23
+ ],
24
+ "sourceMap": true,
25
+ "instrument": true
11
26
  },
12
27
  "keywords": [],
13
28
  "author": "Björn Kimminich",
@@ -16,6 +31,10 @@
16
31
  "cheerio": "^1.2.0",
17
32
  "commander": "^14.0.3"
18
33
  },
34
+ "devDependencies": {
35
+ "nyc": "^17.1.0",
36
+ "standard": "^17.1.2"
37
+ },
19
38
  "engines": {
20
39
  "node": "20 - 24"
21
40
  }
@@ -0,0 +1,28 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const { request } = require('../src/utils/httpClient')
4
+
5
+ const targets = [
6
+ { id: '16215', url: 'https://unityleague.gg/player/16215/', file: 'unityLeague.html' },
7
+ { id: '3irvwtmk', url: 'https://mtgeloproject.net/profile/3irvwtmk', file: 'mtgElo.html' },
8
+ { id: 'k0shiii', url: 'https://melee.gg/Profile/Index/k0shiii', file: 'melee.html' },
9
+ { id: 'k0shiii', url: 'https://topdeck.gg/profile/@k0shiii', file: 'topdeck.html' },
10
+ { id: 'm4VSTJShiXR1PCSCWaM9TBY0rcg1', url: 'https://topdeck.gg/profile/m4VSTJShiXR1PCSCWaM9TBY0rcg1/stats', file: 'topdeck.json' }
11
+ ]
12
+
13
+ async function updateTestData () {
14
+ for (const target of targets) {
15
+ console.log(`Fetching ${target.url}...`)
16
+ try {
17
+ const { data } = await request(target.url, { maxRedirects: 10 })
18
+ const filePath = path.join(__dirname, '..', 'test', 'data', target.file)
19
+ fs.writeFileSync(filePath, data)
20
+ console.log(`Updated ${filePath}`)
21
+ } catch (error) {
22
+ console.error(`Error updating ${target.file}:`, error.message)
23
+ process.exit(1)
24
+ }
25
+ }
26
+ }
27
+
28
+ updateTestData()
@@ -1,23 +1,23 @@
1
- const { request } = require('../utils/httpClient');
2
- const cheerio = require('cheerio');
1
+ const { request } = require('../utils/httpClient')
2
+ const cheerio = require('cheerio')
3
3
 
4
4
  class MeleeFetcher {
5
- async fetchById(username) {
6
- const url = `https://melee.gg/Profile/Index/${username}`;
5
+ async fetchById (username) {
6
+ const url = `https://melee.gg/Profile/Index/${username}`
7
7
  try {
8
- const { data } = await request(url);
9
- return this.parseHtml(data, url, username);
8
+ const { data } = await request(url)
9
+ return this.parseHtml(data, url, username)
10
10
  } catch (error) {
11
- console.error(`Error fetching Melee profile ${username}:`, error.message);
12
- return null;
11
+ console.error(`Error fetching Melee profile ${username}:`, error.message)
12
+ return null
13
13
  }
14
14
  }
15
15
 
16
- parseHtml(html, url, username) {
17
- const $ = cheerio.load(html);
18
- const name = $('span[style*="font-size: xx-large"]').first().text().trim() || username;
19
- const pronouns = $('.profile-details span.text-muted.mr-2').filter((i, el) => $(el).text().includes('/')).first().text().trim();
20
- const bio = $('.profile-details div[style*="max-width: 75%"]').first().text().trim();
16
+ parseHtml (html, url, username) {
17
+ const $ = cheerio.load(html)
18
+ const name = $('span[style*="font-size: xx-large"]').first().text().trim() || username
19
+ const pronouns = $('.profile-details span.text-muted.mr-2').filter((i, el) => $(el).text().includes('/')).first().text().trim()
20
+ const bio = $('.profile-details div[style*="max-width: 75%"]').first().text().trim()
21
21
  // FIXME Photos cannot be loaded with unauthenticated requests from Melee.gg
22
22
  // const photo = $('.profile-button-column img').first().attr('src') || $('img.m-auto').attr('src');
23
23
 
@@ -26,39 +26,30 @@ class MeleeFetcher {
26
26
  url,
27
27
  name,
28
28
  pronouns: pronouns || null,
29
- bio: bio || null,
29
+ bio: bio || null
30
30
  // photo: photo ? (photo.startsWith('http') ? photo : `https://melee.gg${photo}`) : null
31
- };
31
+ }
32
32
 
33
33
  $('.social-link').each((i, el) => {
34
- const href = $(el).attr('href');
34
+ const href = $(el).attr('href')
35
35
  if (href) {
36
36
  try {
37
- const urlObj = new URL(href);
38
- const platform = urlObj.hostname.replace('www.', '').split('.')[0];
39
- let handle = urlObj.pathname.split('/').filter(Boolean).pop();
40
- if (handle) {
41
- handle = decodeURIComponent(handle);
42
- }
43
- if (platform === 'youtube' && handle.startsWith('@')) {
44
- // keep @ for youtube
45
- } else if (platform === 'facebook') {
46
- // handle is correct
47
- } else if (platform === 'twitch') {
48
- // handle is correct
49
- }
37
+ const urlObj = new URL(href)
38
+ const platform = urlObj.hostname.replace('www.', '').split('.')[0]
39
+ let handle = urlObj.pathname.split('/').filter(Boolean).pop()
50
40
  if (handle) {
51
- const label = platform.charAt(0).toLowerCase() + platform.slice(1);
52
- data[label] = handle;
41
+ handle = decodeURIComponent(handle)
42
+ const label = platform.charAt(0).toLowerCase() + platform.slice(1)
43
+ data[label] = handle
53
44
  }
54
45
  } catch (e) {
55
- // ignore invalid URLs
46
+ console.log('Invalid URL in social link ' + href + ': ' + e.message)
56
47
  }
57
48
  }
58
- });
49
+ })
59
50
 
60
- return data;
51
+ return data
61
52
  }
62
53
  }
63
54
 
64
- module.exports = MeleeFetcher;
55
+ module.exports = MeleeFetcher
@@ -1,85 +1,85 @@
1
- const { request } = require('../utils/httpClient');
1
+ const { request } = require('../utils/httpClient')
2
2
 
3
3
  class MtgEloFetcher {
4
- async fetchById(id) {
5
- const url = `https://mtgeloproject.net/profile/${id}`;
4
+ async fetchById (id) {
5
+ const url = `https://mtgeloproject.net/profile/${id}`
6
6
  try {
7
7
  const { data: html } = await request(url, {
8
8
  maxRedirects: 10
9
- });
9
+ })
10
10
 
11
- return this.parseHtml(html, url, id);
11
+ return this.parseHtml(html, url, id)
12
12
  } catch (error) {
13
- console.error(`Error fetching MTG Elo Project profile ${id}:`, error.message);
14
- return null;
13
+ console.error(`Error fetching MTG Elo Project profile ${id}:`, error.message)
14
+ return null
15
15
  }
16
16
  }
17
17
 
18
- parseHtml(html, url, id) {
19
- const cheerio = require('cheerio');
20
- const $ = cheerio.load(html);
18
+ parseHtml (html, url, id) {
19
+ const cheerio = require('cheerio')
20
+ const $ = cheerio.load(html)
21
21
 
22
- let name = '';
23
- let currentRating = '';
24
- let record = '';
22
+ let name = ''
23
+ let currentRating = ''
24
+ let record = ''
25
25
 
26
- const astroIsland = $('astro-island[component-url*="Profile"]');
26
+ const astroIsland = $('astro-island[component-url*="Profile"]')
27
27
  if (astroIsland.length > 0) {
28
28
  try {
29
- const props = JSON.parse(astroIsland.attr('props'));
30
- const info = props.info[1];
31
- name = `${info.first_name[1]} ${info.last_name[1]}`;
32
- currentRating = Math.round(info.current_rating[1]).toString();
33
- const r = info.record[1];
34
- record = `${r[0][1]}-${r[1][1]}-${r[2][1]}`;
29
+ const props = JSON.parse(astroIsland.attr('props'))
30
+ const info = props.info[1]
31
+ name = `${info.first_name[1]} ${info.last_name[1]}`
32
+ currentRating = Math.round(info.current_rating[1]).toString()
33
+ const r = info.record[1]
34
+ record = `${r[0][1]}-${r[1][1]}-${r[2][1]}`
35
35
  } catch (e) {
36
- console.error('Error parsing MTG Elo props:', e.message);
36
+ console.error('Error parsing MTG Elo props:', e.message)
37
37
  }
38
38
  }
39
39
 
40
40
  if (!name) {
41
- name = $('.text-\\[22pt\\]').text().trim();
41
+ name = $('.text-\\[22pt\\]').text().trim()
42
42
  if (name.includes(',')) {
43
- const parts = name.split(',').map(s => s.trim());
44
- name = `${parts[1]} ${parts[0]}`;
43
+ const parts = name.split(',').map(s => s.trim())
44
+ name = `${parts[1]} ${parts[0]}`
45
45
  }
46
46
  }
47
47
 
48
48
  if (!currentRating) {
49
- currentRating = $('.text-\\[18pt\\]:contains("Current rating")').find('.font-bold').text().trim();
49
+ currentRating = $('.text-\\[18pt\\]:contains("Current rating")').find('.font-bold').text().trim()
50
50
  }
51
51
 
52
52
  if (!record) {
53
- const recordText = $('.text-\\[18pt\\]:contains("Record")').text();
54
- record = recordText.replace('Record:', '').trim();
53
+ const recordText = $('.text-\\[18pt\\]:contains("Record")').text()
54
+ record = recordText.replace('Record:', '').trim()
55
55
  }
56
56
 
57
- if (!name) return null;
57
+ if (!name) return null
58
58
 
59
59
  const data = {
60
60
  source: 'MTG Elo Project',
61
- url: url,
62
- name: name,
61
+ url,
62
+ name,
63
63
  player_id: id,
64
64
  current_rating: currentRating,
65
- record: record
66
- };
65
+ record
66
+ }
67
67
 
68
68
  if (record && record.includes('-')) {
69
- const [w, l, d] = record.split('-').map(Number);
69
+ const [w, l, d] = record.split('-').map(Number)
70
70
  if (!isNaN(w) && !isNaN(l)) {
71
- const wins = w;
72
- const losses = l;
73
- const draws = isNaN(d) ? 0 : d;
74
- const total = wins + losses + draws;
71
+ const wins = w
72
+ const losses = l
73
+ const draws = isNaN(d) ? 0 : d
74
+ const total = wins + losses + draws
75
75
  if (total > 0) {
76
- data['win rate'] = ((wins / total) * 100).toFixed(2) + '%';
76
+ data['win rate'] = ((wins / total) * 100).toFixed(2) + '%'
77
77
  }
78
78
  }
79
79
  }
80
80
 
81
- return data;
81
+ return data
82
82
  }
83
83
  }
84
84
 
85
- module.exports = MtgEloFetcher;
85
+ module.exports = MtgEloFetcher