Сайт artifactcube.com разработал свою версию скрипта, представленного в оригинальной новости. Теперь информация может отображаться на страницах сайта: доступны различные фильтры и сортировки, а самое главное ей можно делиться с другими игроками. Вот так выглядит профиль со статистикой на сайте.

TH6xFlo.png

Фрагмент профиля

Порядок действий для создания своего профиля со статистикой Artifact:
  1. Переходим на artifactcube.com (впрочем, действия можно выполнить на любой странице сайта)
  2. Нажимаем на ссылку "Click here to copy the script" (если это не сработает, нужно перейти по второй ссылке и скопировать код самостоятельно)
  3. Переходим на свою страницу со статистикой в Steam, вставляем ранее скопированный код в консоль браузера (сочетанием клавиш Ctrl+Shift+I или кликнув в меню Chrome Дополнительные инструменты > Инструменты разработчика) и нажимаем Enter
  4. Когда скрипт закончит парсить ваши матчи, скопировать код из открывшегося окна в текстовое поле на сайте

    1546573863629.png

    Полное содержимое этого окна и надо скопировать на artifactcube.com
  5. Сайт обработает полученную информацию и сгенерирует вашу страницу-профиль

Оригинальная новость:
Несмотря на то, что в 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 матчей ушло больше пяти минут).
      1545914738974.png
    • Важно не обновлять и не закрывать страницу — иначе придётся начинать с начала
    • Скрипт не получает доступа к чувствительной информации (логину/паролю/дк хуку в инвентаре/и т.д.), а также не отправляет её на сторонние сервера
  • После завершения работы на той же странице появится вся нужная нам информация

    sTASKDW.png

    Фрагмент данных, обработанных скриптом
Расшифровка:
  • Полученная информация достаточно понятна даже без знаний языков (Constructed это режим "Личная колода", а перевести названия героев с английского на русский поможет наша библиотека карт — в строку поиска можно вводить английские названия, даже если сами карты отображаются на русском)
  • Автор не смог определить названия состязаний #7 и #10 (в списке они отображаются как gauntlet #7 и gauntlet #10). Можно предположить, что это платный/бесплатный констрактед

Источник