Сайт artifactcube.com разработал свою версию скрипта, представленного в оригинальной новости. Теперь информация может отображаться на страницах сайта: доступны различные фильтры и сортировки, а самое главное ей можно делиться с другими игроками. Вот так выглядит профиль со статистикой на сайте.
Фрагмент профиля
Порядок действий для создания своего профиля со статистикой Artifact:
Оригинальная новость:
Несмотря на то, что в Artifact нет внутриигрового профиля со статистикой, а также официального API для этой информации, все матчи записываются в разделе личной статистики Steam. Пользователь reddit создал скрипт, который позволяет преобразовать этот массив информации в удобный для восприятия вид.
Порядок действий (работа скрипта проверена только в браузере Chrome):
Источник
Фрагмент профиля
Порядок действий для создания своего профиля со статистикой Artifact:
- Переходим на artifactcube.com (впрочем, действия можно выполнить на любой странице сайта)
- Нажимаем на ссылку "Click here to copy the script" (если это не сработает, нужно перейти по второй ссылке и скопировать код самостоятельно)
- Переходим на свою страницу со статистикой в Steam, вставляем ранее скопированный код в консоль браузера (сочетанием клавиш Ctrl+Shift+I или кликнув в меню Chrome Дополнительные инструменты > Инструменты разработчика) и нажимаем Enter
- Когда скрипт закончит парсить ваши матчи, скопировать код из открывшегося окна в текстовое поле на сайте
Полное содержимое этого окна и надо скопировать на artifactcube.com - Сайт обработает полученную информацию и сгенерирует вашу страницу-профиль
Оригинальная новость:
Несмотря на то, что в Artifact нет внутриигрового профиля со статистикой, а также официального API для этой информации, все матчи записываются в разделе личной статистики Steam. Пользователь reddit создал скрипт, который позволяет преобразовать этот массив информации в удобный для восприятия вид.
Порядок действий (работа скрипта проверена только в браузере Chrome):
- Перейти по ссылке https://steamcommunity.com/my/gcpd/583950?category=Games&tab=MatchPlayers
- Открыть консоль (сочетанием клавиш Ctrl+Shift+I или кликнуть в меню Chrome Дополнительные инструменты > Инструменты разработчика)
- Вставить в консоль следующий код и нажать Enter
Код:var loadingDiv = document.createElement("div");loadingDiv.style="background:white ;border: 1px solid Black; position: fixed; top: 50%; left: 50%; padding: 20px; z-index: 100; display:flex; flex-direction: row; align-items: center;"; document.body.append(loadingDiv); var loadingImg = document.createElement("img"); loadingImg.src="https://steamcommunity-a.akamaihd.net/public/images/login/throbber.gif"; var loadingText = document.createElement("div"); loadingText.textContent = "loading matches please wait..."; loadingText.style = "margin-right: 10px;"; loadingDiv.append(loadingText); loadingDiv.append(loadingImg); hero_data = {'Ogre Magi': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10043.4b52a60db821028ba02a7fb728988bec2842397f.png'}, 'Keefe the Bold': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set00/4003.2cc81eb5cf6375fdfad19781634a37df496e20c0.png'}, 'Skywrath Mage': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10062.320871aec38db2b973d6fa8933e62d3f44e8e407.png'}, 'Timbersaw': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10022.065c0b1ce65b1f201bca225121177c2d19df2c89.png'}, 'Magnus': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10067.9130bf7958f290e69cf5bcc960332d44830919ae.png'}, 'Omniknight': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10044.5fe4f8195b2a005f6b40ab273b2504782e84d191.png'}, 'Viper': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10028.50b8bc7588cab219f86ac87014b2c394d0ee5754.png'}, 'Storm Spirit': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10536.f29d6a820bf65991ea122a1efdb61088651d2706.png'}, 'Ursa': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10070.0fb457ff9ef3010b7f2743789889339aa74b4152.png'}, 'Prellex': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10053.46d6ca300f26d8e05aaf9d63c590508a723415b5.png'}, 'Lycan': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10014.d0b682f37b2b7b777078f811bdf93a6fa5231ac5.png'}, 'Sniper': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10050.be0a865d3d49ca386d2a3835cc46eab52a923f47.png'}, 'Meepo': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10004.8e77e6463c971dab1fa01b3761808eb2cf5163b0.png'}, 'Winter Wyvern': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10010.ad0a64e7f9814fc0cb1c7e2aa4b217e549239bb6.png'}, 'Venomancer': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10001.c2456641c9dd1b758dd7a0caf5e2a56f727a51f8.png'}, 'Crystal Maiden': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10064.e76bb6f2b597ae16bc8a5efa82eca5ff39afaa29.png'}, 'Zeus': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10065.de19a6f1e68cfcab2f38f25b3d8847866ba87ccb.png'}, 'Beastmaster': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10029.9deae28f2a6715b2456e5844e2fdea7aaa28418f.png'}, 'Lion': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10060.1b8ab00d3d43fa0aec13c1d4f078c510c2828eed.png'}, 'Bloodseeker': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10018.99a88322fecd54fc5fe195cd8a12f49bc64e424c.png'}, 'Sven': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10054.f7ec19918c68fa805f4577fd31457b5a8a7da2e5.png'}, 'Tinker': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10025.33033b1e8b19d9a934322355077aef6467115d73.png'}, 'Drow Ranger': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10032.ac3532fc6482ff603748ddde2fb3564273a3b72e.png'}, "J'Muy the Wise": {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set00/4008.35f4da08e371db6ecc077f4aad08113dac8cc4de.png'}, 'Centaur Warrunner': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10021.f655334174b3964b58604e3cfa80360e06d83004.png'}, 'Treant Protector': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10056.4ea3388b448c9ee13bbbfd7248b46f0c3e71e98a.png'}, 'Farvhan the Dreamer': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set00/4000.049cff338ab7274d0dcde4d4e3ec5bc5bcdd2a8e.png'}, 'Dark Seer': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10068.e8e173ac8dd4f1d582fe41378232b4c3637ab039.png'}, 'Kanna': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10031.967b53755c3a477cc89b05422018a06d08e837f2.png'}, 'Outworld Devourer': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10046.959caac2a5c3a0e8efeec6af631337a483bb8795.png'}, 'Abaddon': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10016.49550e3b17ccf2611eb71b22f58664ae338be8ae.png'}, 'Axe': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10020.023febd622949d771d9f6a4322efc339ced8c560.png'}, 'Debbi the Cunning': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set00/4005.ceab77c296987994f9dfbc5f27bfcbb3fefde14a.png'}, 'Bristleback': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10030.d6427ab9016cda608a66759ed0b5e8a51a532d0f.png'}, 'Lich': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10038.26778b9a203a57b0d3ffb1bc75b7a0734e88c6c8.png'}, 'Earthshaker': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10033.7c8e9fa3eccd923d8433ed2b98aef717766ca0d9.png'}, 'Pugna': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10048.ca73a1b73aa37fc74a7807a69357e2e5641b01df.png'}, 'Rix': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10026.59c2df4317db54a08c492c3d78f23ed9206a2a17.png'}, 'Legion Commander': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10069.f0595417e162f98c02cd6ce7b3a45b5413f6639f.png'}, 'Mazzie': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10052.16b67615c0229d72fd946e7b9e2407420bbbf67f.png'}, 'Bounty Hunter': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10023.503fcb60e0cdc30aa1d5a6f858a4050e3315b5f0.png'}, 'Luna': {'color': 'blue', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10006.ee9329205440996f09bdcc600c6aaeaa8440c44b.png'}, 'Tidehunter': {'color': 'red', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10024.1ffefd6bce93c5753f51eefdd39e1089b810b08b.png'}, 'Sorla Khan': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10058.635583094b11fcc0f86d624e9007f4a519179f38.png'}, 'Phantom Assassin': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10047.dd94f5bc1b22bef2e9b18264c523d8e28704f728.png'}, 'Chen': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10017.c755182fd873b5df7f4d9bf6075dc1065021b560.png'}, 'Enchantress': {'color': 'green', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10036.995dc56212e8e8bf40471d3121262d43a82e7c31.png'}, 'Necrophos': {'color': 'black', 'image': 'https://steamcdn-a.akamaihd.net/apps/583950/icons/set01/10059.66aae3f23e16000eb90086810be1d750ec6babe0.png'}} div_color = { "black": "#999999", "red": "#aa3333", "green": "#33aa33", "blue" : "#6666aa" } matches = {}; var matchCounter = 0; var failures = 0; function GetNextData() { if (g_sGcContinueToken == null || g_sGcContinueToken == undefined) { ShowAlertDialog( 'Error', 'Failed because the history token is missing. Please inform the script creator.', 'OK' ); } if (g_sessionID == null || g_sessionID == undefined) { ShowAlertDialog( 'Error', 'Failed because the session token is missing. Please inform the script creator.', 'OK' ); } var request_data = { ajax: 1, tab: 'MatchPlayers', continue_token: g_sGcContinueToken, sessionid: g_sessionID }; $J.ajax({ type: "GET", url: "https://steamcommunity.com/my/gcpd/583950", data: request_data }).done( function( data ) { if ( data.success ) { if ( data.html ) { var resultDataDOM = new DOMParser().parseFromString( data.html , 'text/html').body.firstChild; var tableRow = resultDataDOM.rows[resultDataDOM.rows.length-1]; var matchID = tableRow.cells[0].textContent; var matchTime = tableRow.cells[3].textContent; var outcome = tableRow.cells[5].textContent; var turns = tableRow.cells[6].textContent; var team = tableRow.cells[9].textContent; var tower1 = tableRow.cells[11].textContent; var tower2 = tableRow.cells[12].textContent; var tower3 = tableRow.cells[13].textContent; var ancient = tableRow.cells[14].textContent; var timeLeft = tableRow.cells[15].textContent; var hero1 = tableRow.cells[16].textContent; var hero2 = tableRow.cells[17].textContent; var hero3 = tableRow.cells[18].textContent; var hero4 = tableRow.cells[19].textContent; var hero5 = tableRow.cells[20].textContent; var gauntletID = tableRow.cells[21].textContent; match = {}; match["matchID"] = matchID; match["matchTime"] = matchTime; match["outcome"] = outcome; match["turns"] = turns; match["team"] = team; match["tower1"] = tower1; match["tower2"] = tower2; match["tower3"] = tower3; match["ancient"] = ancient; match["timeLeft"] = timeLeft; match["hero1"] = hero1; match["hero2"] = hero2; match["hero3"] = hero3; match["hero4"] = hero4; match["hero5"] = hero5; match["gauntletID"] = gauntletID; match["isWin"] = outcome == team; if (!(hero1 == "0") && !(hero2 == "0") && !(hero3 == "0") && !(hero4 == "0") && !(hero5 == "0")) { console.log("------------ match ----------"); for (var key in match){ console.log(key + ": " + match[key]); } if (matches[gauntletID] == undefined) { matches[gauntletID] = [] } matches[gauntletID].push(match); matchCounter ++; } } failures = 0; if ( data.continue_token ) { g_sGcContinueToken = data.continue_token; loadingText.textContent = "loading matches please wait... (" + matchCounter + " loaded)"; setTimeout(GetNextData, 100); } else { g_sGcContinueToken = null; setTimeout(processData, 100); } } else { failures++; if (failures > 5) { ShowAlertDialog( 'Error', 'Artifact servers have returned invalid data. Sorry. Please try again.', 'OK' ); } else { setTimeout(GetNextData, 1000); } } }).fail( function( jqXHR ) { if ( jqXHR.status == 429 ) { failures++; if (failures > 5) { ShowAlertDialog( 'Error', 'Artifact servers have rate-limited you', 'OK' ); } else { setTimeout(GetNextData, 1000); } } else { failures++; if (failures > 5) { ShowAlertDialog( 'Error', 'Artifact serves have returned an error. Sorry. Please try again.', 'OK' ); } else { setTimeout(GetNextData, 1000); } } }); } GetNextData(); function processData() { loadingDiv.style = "display: none"; mainContents = document.getElementById("mainContents"); var resultsDiv = document.createElement("div"); mainContents.insertBefore(resultsDiv, mainContents.firstChild); for (var gauntletID in matches) { var gauntletDiv = document.createElement("div"); resultsDiv.append(gauntletDiv); gauntletDiv.style = "margin-top: 20px;" if (gauntletID == 0) { gauntletDiv.textContent = "-------------------------- Constructed --------------------------"; } else if (gauntletID == 8) { gauntletDiv.textContent = "-------------------------- Prize Phantom Draft --------------------------"; } else if (gauntletID == 11) { gauntletDiv.textContent = "-------------------------- Standard Phantom Draft --------------------------"; } else if (gauntletID == 5) { gauntletDiv.textContent = "-------------------------- Call To Arms --------------------------"; } else if (gauntletID == 9) { gauntletDiv.textContent = "-------------------------- Keeper Draft --------------------------"; } else { gauntletDiv.textContent = "------------- gauntlet #" + gauntletID + " (unknown, let script creator know what mode you think this is) ------------------"; } gauntletMatches = matches[gauntletID]; var winCount = 0; var i = 0; for (i = 0; i < gauntletMatches.length; i++) { var match = gauntletMatches[i]; if (match["isWin"] == true) { winCount ++; } } var gamesDiv = document.createElement("div"); resultsDiv.append(gamesDiv); gamesDiv.textContent = "Games Played: " + gauntletMatches.length; var winsDiv = document.createElement("div"); resultsDiv.append(winsDiv); winsDiv.textContent = "Win rate: " + (winCount * 100/gauntletMatches.length).toFixed(3) + "%"; var hero_stats = {} for (i = 0; i < gauntletMatches.length; i++) { var match = gauntletMatches[i]; for (var j = 1; j <= 5; j++) { hero = match["hero"+j]; if (hero_stats[hero] == undefined) { hero_stats[hero] = {}; hero_stats[hero]["num_games"] = 0; hero_stats[hero]["num_wins"] = 0; } hero_stats[hero]["num_games"]++; if (match["isWin"] == true) { hero_stats[hero]["num_wins"]++; } } } var heroGames = []; var heroWinRates = []; for (var hero in hero_stats) { heroGames.push([hero, hero_stats[hero]["num_games"]]); heroWinRates.push([hero, hero_stats[hero]["num_games"], hero_stats[hero]["num_wins"]/hero_stats[hero]["num_games"]]); } heroGames.sort(function(a, b) { return b[1] - a[1]; }); heroWinRates.sort(function(a, b) { return b[2] - a[2]; }); var heroGamesDiv = document.createElement("div"); resultsDiv.append(heroGamesDiv); heroGamesDiv.style = "margin-top: 10px;" heroGamesDiv.textContent = " --- Top heroes played ---"; for (i = 0; i < heroGames.length; i++) { var heroGamesDiv = document.createElement("div"); resultsDiv.append(heroGamesDiv); heroGamesDiv.style = "color: " + div_color[hero_data[heroGames[i][0]].color]; heroGamesDiv.textContent = heroGames[i][1] + " - " + heroGames[i][0]; } var heroGamesDiv = document.createElement("div"); resultsDiv.append(heroGamesDiv); heroGamesDiv.style = "margin-top: 10px;" heroGamesDiv.textContent = " --- Top heroes win rate ---"; for (i = 0; i < heroWinRates.length; i++) { var heroGamesDiv = document.createElement("div"); resultsDiv.append(heroGamesDiv); heroGamesDiv.style = "color: " + div_color[hero_data[heroWinRates[i][0]].color]; heroGamesDiv.textContent = (heroWinRates[i][2]*100).toFixed(1) + "% - " + heroWinRates[i][0] + " (games played: " + heroWinRates[i][1] + " )"; } var matchHistoryDiv = document.createElement("div"); resultsDiv.append(matchHistoryDiv); matchHistoryDiv.style = "margin-top: 10px;" matchHistoryDiv.textContent = " --- Condensed match history ---"; for (i = 0; i < gauntletMatches.length; i++) { var match = gauntletMatches[i]; var matchDiv = document.createElement("div"); resultsDiv.append(matchDiv); matchDiv.textContent = (match["isWin"] ? "win" : "loss") + " ---- " + match["hero1"] + ", " + match["hero2"] + ", " + match["hero3"] + ", " + match["hero4"] + ", " + match["hero5"]; matchDiv.style = match["isWin"] ? "color:green" : "color:red" /*var heroImg = document.createElement("img"); heroImg.src = hero_data[match.hero1].image; resultsDiv.append(heroImg);*/ } } }
- Скрипт начнёт обрабатывать матчи. В зависимости от их количества, это может занять значительное время (на почти 300 матчей ушло больше пяти минут).
- Важно не обновлять и не закрывать страницу — иначе придётся начинать с начала
- Скрипт не получает доступа к чувствительной информации (логину/паролю/дк хуку в инвентаре/и т.д.), а также не отправляет её на сторонние сервера
- Скрипт начнёт обрабатывать матчи. В зависимости от их количества, это может занять значительное время (на почти 300 матчей ушло больше пяти минут).
- После завершения работы на той же странице появится вся нужная нам информация
Фрагмент данных, обработанных скриптом
- Полученная информация достаточно понятна даже без знаний языков (Constructed это режим "Личная колода", а перевести названия героев с английского на русский поможет наша библиотека карт — в строку поиска можно вводить английские названия, даже если сами карты отображаются на русском)
- Автор не смог определить названия состязаний #7 и #10 (в списке они отображаются как gauntlet #7 и gauntlet #10). Можно предположить, что это платный/бесплатный констрактед