您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
支持自动填写 Steam 令牌验证码和批量确认交易与市场
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/469857/1213302/Steam%20%E4%BB%A4%E7%89%8C%E9%AA%8C%E8%AF%81%E5%99%A8.js
// ==UserScript== // @name Steam 令牌验证器 // @namespace https://keylol.com/t652195-1-1 // @version 0.7.2 // @description 支持自动填写 Steam 令牌验证码和批量确认交易与市场 // @author wave // @match http*://store.steampowered.com/* // @match http*://help.steampowered.com/* // @match http*://steamcommunity.com/* // @match http*://checkout.steampowered.com/* // @match *://keylol.com/steamconf* // @grant GM_setValue // @grant GM_getValue // @grant GM_setClipboard // @grant GM_addStyle // @grant GM_addValueChangeListener // @grant GM_xmlhttpRequest // @grant unsafeWindow // @connect steampowered.com // @connect steamcommunity.com // @require https://bundle.run/[email protected] // @require https://unpkg.com/crypto-js/crypto-js.js // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGJhc2VQcm9maWxlPSJ0aW55IiB2aWV3Qm94PSIwIDAgMzIgMzIiICB4bWxuczp2PSJodHRwczovL3ZlY3RhLmlvL25hbm8iPjxnIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjUiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCI+PHBhdGggZD0iTTI4LjggMzAuOEgzLjNhMS45NyAxLjk3IDAgMCAxLTEuOS0xLjlWMTAuN2ExLjk3IDEuOTcgMCAwIDEgMS45LTEuOWgyNS41YTEuOTcgMS45NyAwIDAgMSAxLjkgMS45VjI5YzAgMS0uOSAxLjgtMS45IDEuOHoiLz48cGF0aCBkPSJNMjIgOC44VjUuN2MwLTIuNC0yLjEtNC40LTQuNi00LjRoLTMuMWMtMi41IDAtNC42IDItNC42IDQuNHYzLjFIMjJ6Ii8+PC9nPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xNy42IDE1LjZzMCAuMSAwIDBsLTMuOSA1LjdhNi4yOCA2LjI4IDAgMCAwLTEuOS4zYy0uMy4xLS41LjItLjcuNGwtOC42LTMuNXMtLjkgMy40IDAgNS44bDYuNyAyLjNhNC40NiA0LjQ2IDAgMCAwIDIuNiAzLjFjMi4zLjkgNC45LS4xIDUuOC0yLjMuMi0uNi40LTEuMi4zLTEuOGw1LjYtMy45aC4xYzMuNCAwIDYuMS0yLjcgNi4xLTZzLTIuNy02LTYuMS02Yy0zLjMtLjEtNiAyLjYtNiA1Ljl6bS0xIDExLjNjLS43IDEuNy0yLjcgMi41LTQuNSAxLjgtLjgtLjMtMS40LS45LTEuOC0xLjdsMS41LjdjMS4zLjUgMi44LS4xIDMuMy0xLjMuNS0xLjMtLjEtMi43LTEuNC0zLjJsLTEuNi0uN2MuOC0uMyAxLjctLjMgMi41IDBhMy4xOSAzLjE5IDAgMCAxIDEuOCAxLjhjLjYuOS42IDEuOC4yIDIuNm03LjEtNy4zYy0yLjIgMC00LjEtMS44LTQuMS00czEuOC00IDQuMS00YzIuMiAwIDQuMSAxLjggNC4xIDQtLjEgMi4yLTEuOSA0LTQuMSA0bS0yLjctNGEyLjY5IDIuNjkgMCAxIDEgNS40IDAgMi42OSAyLjY5IDAgMCAxLTIuNyAyLjdjLTEuNS0uMS0yLjctMS4yLTIuNy0yLjd6Ii8+PC9zdmc+ // ==/UserScript== (function() { var util = (function () { function util() {} util.xhr = function (xhrData) { return new Promise(function(resolve, reject) { if (!xhrData.xhr) { GM_xmlhttpRequest({ method: xhrData.method || "get", url: xhrData.url, data: xhrData.data, headers: xhrData.headers || {}, responseType: xhrData.type || "", timeout: 3e5, onload: function onload(res) { return resolve({ response: res, body: res.response }); }, onerror: reject, ontimeout: reject }); } else { var xhr = new XMLHttpRequest(); xhr.open(xhrData.method || "get", xhrData.url, true); if (xhrData.method === "post") {xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded; charset=utf-8");} if (xhrData.cookie) {xhr.withCredentials = true;} xhr.responseType = xhrData.type || ""; xhr.timeout = 3e5; if (xhrData.headers) {for (var k in xhrData.headers) {xhr.setRequestHeader(k, xhrData.headers[k]);}} xhr.onload = function(ev) { var evt = ev.target; resolve({ response: evt, body: evt.response }); }; xhr.onerror = reject; xhr.ontimeout = reject; xhr.send(xhrData.data); } }); }; util.createElement = function (data) { var node; if (data.node) { node = document.createElement(data.node); if (data.content) {this.setElement({node: node, content: data.content});} if (data.html) {node.innerHTML = data.html;} } return node; }; util.setElement = function (data) { if (data.node) { for (let name in data.content) {data.node.setAttribute(name, data.content[name]);} if (data.html!=undefined) {data.node.innerHTML = data.html;} } }; return util; })(); function bufferizeSecret(secret) { if (typeof secret === 'string') { // Check if it's hex if (secret.match(/[0-9a-f]{40}/i)) { return buffer.Buffer.from(secret, 'hex'); } else { // Looks like it's base64 return buffer.Buffer.from(secret, 'base64'); } } return secret; } function generateAuthCode(secret, timeOffset) { secret = bufferizeSecret(secret); let time = Math.floor(Date.now() / 1000) + (timeOffset || 0); let b = buffer.Buffer.allocUnsafe(8); b.writeUInt32BE(0, 0); // This will stop working in 2038! b.writeUInt32BE(Math.floor(time / 30), 4); let hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1, CryptoJS.lib.WordArray.create(secret)); hmac = buffer.Buffer.from(hmac.update(CryptoJS.lib.WordArray.create(b)).finalize().toString(CryptoJS.enc.Hex), 'hex'); let start = hmac[19] & 0x0F; hmac = hmac.slice(start, start + 4); let fullcode = hmac.readUInt32BE(0) & 0x7FFFFFFF; const chars = '23456789BCDFGHJKMNPQRTVWXY'; let code = ''; for (let i = 0; i < 5; i++) { code += chars.charAt(fullcode % chars.length); fullcode /= chars.length; } return code; }; function generateConfirmationKey(identitySecret, time, tag) { identitySecret = bufferizeSecret(identitySecret); let dataLen = 8; if (tag) { if (tag.length > 32) { dataLen += 32; } else { dataLen += tag.length; } } let b = buffer.Buffer.allocUnsafe(dataLen); b.writeUInt32BE(0, 0); b.writeUInt32BE(time, 4); if (tag) { b.write(tag, 8); } let hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1, CryptoJS.lib.WordArray.create(identitySecret)); return hmac.update(CryptoJS.lib.WordArray.create(b)).finalize().toString(CryptoJS.enc.Base64); }; function getDeviceID(steamID) { let salt = ''; return "android:" + CryptoJS.SHA1(steamID.toString() + salt).toString(CryptoJS.enc.Hex).replace(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12}).*$/, '$1-$2-$3-$4-$5'); }; function generateConfirmationQueryParams(account, tag, timeOffset) { var time = Math.floor(Date.now() / 1000) + (timeOffset || 0); var key = generateConfirmationKey(account.identitySecret, time, tag); var deviceID = getDeviceID(account.steamID); return 'a=' + account.steamID + '&tag=' + tag + '&m=react&t=' + time + '&p=' + encodeURIComponent(deviceID) + '&k=' + encodeURIComponent(key); } function showAddAccountDialog(strTitle, strOKButton, strCancelButton, rgModalParams) { if (!strOKButton) { strOKButton = '确定'; } if (!strCancelButton) { strCancelButton = '取消'; } var $Body = $J('<form/>'); var $AccountNameInput = $J('<input/>', {type: 'text', 'class': ''}); var $SharedSecretInput = $J('<input/>', {type: 'text', 'class': ''}); var $SteamIDInput = $J('<input/>', {type: 'text', 'class': ''}); var $IdentitySecretInput = $J('<input/>', {type: 'text', 'class': ''}); if (rgModalParams && rgModalParams.inputMaxSize) { $AccountNameInput.attr('maxlength', rgModalParams.inputMaxSize); $SharedSecretInput.attr('maxlength', rgModalParams.inputMaxSize); $SteamIDInput.attr('maxlength', rgModalParams.inputMaxSize); $IdentitySecretInput.attr('maxlength', rgModalParams.inputMaxSize); } $Body.append($J('<div/>', {'class': 'newmodal_prompt_description'}).append('Steam 帐户名称<span data-tooltip-text="非个人资料名称,用于自动填写 Steam 令牌验证码。"> (?)</span>')); $Body.append($J('<div/>', {'class': 'newmodal_prompt_input gray_bevel for_text_input fullwidth'}).append($AccountNameInput)); $Body.append($J('<div/>', {'class': 'newmodal_prompt_description', 'style': 'margin-top: 8px;'}).append('共享密钥<span data-tooltip-text="即 shared secret,用于生成 Steam 令牌验证码。"> (?)</span>')); $Body.append($J('<div/>', {'class': 'newmodal_prompt_input gray_bevel for_text_input fullwidth'}).append($SharedSecretInput)); $Body.append($J('<div/>', {'class': 'newmodal_prompt_description', 'style': 'margin-top: 8px;'}).append('64 位 Steam ID<span data-tooltip-text="以“7656”开头的 17 位数字,用于确认交易与市场。"> (?)</span>')); $Body.append($J('<div/>', {'class': 'newmodal_prompt_input gray_bevel for_text_input fullwidth'}).append($SteamIDInput)); $Body.append($J('<div/>', {'class': 'newmodal_prompt_description', 'style': 'margin-top: 8px;'}).append('身份密钥<span data-tooltip-text="即 identity secret,用于确认交易与市场。"> (?)</span>')); $Body.append($J('<div/>', {'class': 'newmodal_prompt_input gray_bevel for_text_input fullwidth'}).append($IdentitySecretInput)); var deferred = new jQuery.Deferred(); var fnOK = function() { var name = $AccountNameInput.val().trim(); var secret = $SharedSecretInput.val().trim(); var steamID = $SteamIDInput.val().trim(); var identitySecret = $IdentitySecretInput.val().trim(); if (!name) { name = '无名氏'; } if (!secret) { ShowAlertDialog('错误', '请输入有效的共享密钥。', '确定'); return; } if (steamID && steamID.indexOf('7656') != 0 && steamID.length != 17) { ShowAlertDialog('错误', '请输入有效的 64 位 Steam ID。', '确定'); return; } deferred.resolve(name, secret, steamID, identitySecret); }; var fnCancel = function() { deferred.reject(); }; $Body.submit(function(event) { event.preventDefault(); fnOK(); }); var $OKButton = _BuildDialogButton(strOKButton, true); $OKButton.click(fnOK); var $CancelButton = _BuildDialogButton(strCancelButton); $CancelButton.click(fnCancel); var Modal = _BuildDialog(strTitle, $Body, [$OKButton, $CancelButton], fnCancel); if(!rgModalParams || !rgModalParams.bNoPromiseDismiss) { deferred.always(function() { Modal.Dismiss(); }); } Modal.Show(); $AccountNameInput.focus(); // attach the deferred's events to the modal deferred.promise(Modal); return Modal; } function showImportAccountDialog(strTitle, strDescription, strOKButton, strCancelButton, textAreaMaxLength) { if (!strOKButton) { strOKButton = '确定'; } if (!strCancelButton) { strCancelButton = '取消'; } var $Body = $J('<form/>'); var $TextArea = $J('<textarea/>', {'class': 'newmodal_prompt_textarea'}); $TextArea.attr('placeholder', strDescription); if (textAreaMaxLength) { $TextArea.attr('maxlength', textAreaMaxLength); $TextArea.bind('keyup change', function() { var str = $J(this).val(); var mx = parseInt($J(this).attr('maxlength')); if (str.length > mx) { $J(this).val(str.substr(0, mx)); return false; } }); } $Body.append($J('<div/>', {'class': 'newmodal_prompt_with_textarea gray_bevel fullwidth'}).append($TextArea)); var deferred = new jQuery.Deferred(); var fnOK = function() { deferred.resolve($TextArea.val()); }; var fnCancel = function() { deferred.reject(); }; $Body.submit(function(event) { event.preventDefault(); fnOK(); }); var $OKButton = _BuildDialogButton(strOKButton, true); $OKButton.click(fnOK); var $CancelButton = _BuildDialogButton(strCancelButton); $CancelButton.click(fnCancel); var Modal = _BuildDialog(strTitle, $Body, [$OKButton, $CancelButton], fnCancel); deferred.always(function() { Modal.Dismiss(); }); Modal.Show(); $TextArea.focus(); // attach the deferred's events to the modal deferred.promise(Modal); return Modal; } function addAccount() { showAddAccountDialog('添加账户', '确定', '取消').done(function(name, secret, steamID, identitySecret) { if (steamID && identitySecret) { accounts.push({ name, secret, steamID, identitySecret }); GM_setValue('accounts', accounts); ShowAlertDialog('添加账户', '添加成功,该账户支持确认交易与市场。', '确定'); } else { accounts.push({ name, secret }); GM_setValue('accounts', accounts); ShowAlertDialog('添加账户', '添加成功,该账户不支持确认交易与市场。', '确定'); } }); setupTooltips($J('.newmodal')); } function importAccount() { showImportAccountDialog('导入账户', '将要导入的数据粘贴于此', '确定', '取消').done(function(data) { try { data = JSON.parse(data.replace(/("SteamID":)(\d+)/, '$1"$2"')); var name = data.account_name || '无名氏'; var secret = data.shared_secret; var steamID = data.steamid || data.Session && data.Session.SteamID || ''; var identitySecret = data.identity_secret; if (!secret) { ShowAlertDialog('错误', '共享密钥不存在,请检查后再试。', '确定').done(function() { importAccount(); }); return; } if (steamID && identitySecret) { accounts.push({ name, secret, steamID, identitySecret }); GM_setValue('accounts', accounts); ShowAlertDialog('导入账户', '导入成功,该账户支持确认交易与市场。', '确定'); } else { accounts.push({ name, secret }); GM_setValue('accounts', accounts); ShowAlertDialog('导入账户', '导入成功,该账户不支持确认交易与市场。', '确定'); } } catch(err) { ShowAlertDialog('错误', '数据格式有误,请检查后再试。', '确定').done(function() { importAccount(); }); } }); } function deleteAccount(elem) { ShowConfirmDialog('删除账户', '确定删除该账户吗?', '确定', '取消').done(function() { var $Elem = $JFromIDOrElement(elem); if ($Elem.data('id') >= accounts.length) { ShowAlertDialog('错误', '无法删除该账户,请稍后再试。', '确定').done(function() { window.location.reload(); }); } else { var account = accounts.splice($Elem.data('id'), 1)[0]; GM_setValue('accounts', accounts); ShowAlertDialog('删除账户', '删除成功。', '确定'); } }); } function copyAuthCode(elem) { var $Elem = $JFromIDOrElement(elem); GM_setClipboard(generateAuthCode(accounts[$Elem.data('id')].secret, timeOffset)); $Elem.css('width', window.getComputedStyle(elem, null).width).text('复制成功').addClass('copy_success'); setTimeout(function() { $Elem.text($Elem.data('name')).removeClass('copy_success'); }, 1000); } function refreshAccounts() { $AuthenticatorPopupMenu.empty(); $J.each(accounts, function(i, v) { var $AuthenticatorPopupMenuItem = $J('<span/>', {'style': 'display: block; padding: 5px 0 5px 12px; margin-right: 27px; min-width: 50px;', 'data-id': i, 'data-name': v.name, 'data-tooltip-text': '点击复制该账户的验证码'}).append(v.name); var $AuthenticatorDeleteAccount = $J('<span/>', {'class': 'delete_account', 'data-id': i, 'data-tooltip-text': '删除该账户'}); $AuthenticatorPopupMenu.append($J('<a/>', {'class': 'popup_menu_item', 'style': 'position: relative; padding: 0;'}).append($AuthenticatorPopupMenuItem, $AuthenticatorDeleteAccount)); $AuthenticatorPopupMenuItem.on('click', function() { copyAuthCode(this); }); $AuthenticatorDeleteAccount.on('click', function() { deleteAccount(this); }); }); setupTooltips($AuthenticatorPopupMenu); var $AuthenticatorAddAccount = $J('<a/>', {'class': 'popup_menu_item'}).append('添加账户'); $AuthenticatorPopupMenu.append($AuthenticatorAddAccount); $AuthenticatorAddAccount.on('click', function() { addAccount(); }); var $AuthenticatorImportAccount = $J('<a/>', {'class': 'popup_menu_item'}).append('导入账户'); $AuthenticatorPopupMenu.append($AuthenticatorImportAccount); $AuthenticatorImportAccount.on('click', function() { importAccount(); }); } function createConfirmationLink(steamID) { if (!$AuthenticatorPopupMenu.find('.confirmation').length) { $J.each(accounts, function(i, v) { if (v.steamID && steamID == v.steamID) { var $AuthenticatorConfirmation = $J('<a/>', {'class': 'popup_menu_item confirmation'}).append('确认交易与市场'); $AuthenticatorPopupMenu.append($AuthenticatorConfirmation); $AuthenticatorConfirmation.on('click', function() { window.open('https://keylol.com/steamconf/', '确认交易与市场', 'height=790,width=600,resize=yes,scrollbars=yes'); }); return false; } }); } } function setupTooltips(selector) { if (window.location.hostname == 'store.steampowered.com') { BindTooltips(selector, {tooltipCSSClass: 'store_tooltip'}); } else if (window.location.hostname == 'help.steampowered.com') { BindTooltips(selector, {tooltipCSSClass: 'help_tooltip'}); } else if (window.location.hostname == 'steamcommunity.com') { BindTooltips(selector, {tooltipCSSClass: 'community_tooltip'}); } } GM_addStyle(` .delete_account { position: absolute; right: 0; top: 0; padding: 0 7.5px; width: 12px; height: 100%; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAQAAAD8fJRsAAAAkElEQVR4AXWQxWEDMRBFJ6AWArqGmW7G12HMDN0ZFmr4dqKF00rDPGPhycnr/vi9nJVPl2qI7Dd0WZpZEyFEygKhy1CkPsX4JCLlB6OP6jo3eRHxhh3xA+OBLULedCtExDOGcRvM6DZzpP/RxgtR4fDKat/ylPUKpZwao1A769VBDbls3H5WO6KfjVu5YOVJDkyDcoTnvnKRAAAAAElFTkSuQmCC); background-position: center; background-repeat: no-repeat; background-origin: content-box; cursor: pointer; } .copy_success { color: #57cbde !important; } `); var $GlobalActionMenu = $J('#global_action_menu'); var $AuthenticatorLink = $J('<span/>', {'class': 'pulldown global_action_link', 'style': 'display: inline-block; padding-left: 4px; line-height: 25px;', 'onclick': 'ShowMenu(this, "authenticator_dropdown", "right");'}).append('Steam 令牌验证器'); var $AuthenticatorDropdown = $J('<div/>', {'class': 'popup_block_new', 'id': 'authenticator_dropdown', 'style': 'display: none;'}); var $AuthenticatorPopupMenu = $J('<div/>', {'class': 'popup_body popup_menu'}); $GlobalActionMenu.prepend($AuthenticatorDropdown.append($AuthenticatorPopupMenu)); $GlobalActionMenu.prepend($AuthenticatorLink); var accounts = GM_getValue('accounts') || []; refreshAccounts(); GM_addValueChangeListener('accounts', function(name, old_value, new_value, remote) { accounts = new_value; refreshAccounts(); if (userSteamID) { $AuthenticatorPopupMenu.find('.confirmation').remove(); createConfirmationLink(userSteamID); } AlignMenu($AuthenticatorLink, 'authenticator_dropdown', 'right'); }); if (window.location.href.indexOf('https://keylol.com/steamconf')>-1) { var init = async function () { document.querySelector('#load-text').innerHTML = '加载中……'; var xmlstr = await util.xhr({url: 'https://steamcommunity.com/my?xml=1'}); var steamid = (new DOMParser()).parseFromString(xmlstr.body, 'application/xml').getElementsByTagName('steamID64')[0].innerHTML; var account = {}; accounts.forEach(function (v) { if (v.steamID && steamid == v.steamID) { account = v; return false; } }); var data = await util.xhr({ url: 'https://steamcommunity.com/mobileconf/getlist?' + generateConfirmationQueryParams(account, 'list', timeOffset), type: 'json' }).then(res => res.body); var submitbtn = document.querySelector('#submit'); var cancelbtn = document.querySelector('#cancel'); submitbtn.onclick = async function () { var allowData = await util.xhr({ url: 'https://steamcommunity.com/mobileconf/multiajaxop?' + generateConfirmationQueryParams(account, 'accept', timeOffset) + '&op=allow', method: 'POST', data: unsafeWindow.formdata, headers: { 'content-type': `multipart/form-data; boundary=${unsafeWindow.boundary}` }, type: 'json' }).then(res => res.body); if (allowData.success) { alert('确认完成!'); window.location.reload(); } else { alert('确认失败!'); } }; cancelbtn.onclick = async function () { var cancelData = await util.xhr({ url: 'https://steamcommunity.com/mobileconf/multiajaxop?' + generateConfirmationQueryParams(account, 'reject', timeOffset) + '&op=cancel', method: 'POST', data: unsafeWindow.formdata, headers: { 'content-type': `multipart/form-data; boundary=${unsafeWindow.boundary}` }, type: 'json' }).then(res => res.body); if (cancelData.success) { alert('取消完成!'); window.location.reload(); } else { alert('确认失败!'); } }; unsafeWindow.proc_data(data); }; init(); } var intersectionObserver = new IntersectionObserver(function(entries) { if (entries[0].intersectionRatio > 0) { var name = $J('#login_twofactorauth_message_entercode_accountname, [class^="login_SigningInAccountName"], [class^="newlogindialog_AccountName').text(); $J.each(accounts, function(i, v) { if(name == v.name) { var $AuthCodeInput = $J('#twofactorcode_entry, [class^="login_AuthenticatorInputcontainer"] input.DialogInput, [class^="newlogindialog_SegmentedCharacterInput"] input, [class^="segmentedinputs_SegmentedCharacterInput"] input'); var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; nativeInputValueSetter.call($AuthCodeInput[0], generateAuthCode(v.secret, timeOffset)); $AuthCodeInput[0].dispatchEvent(new Event('input', {bubbles: true})); return false; } }); } }); var mutationObserver = new MutationObserver(function() { if ($J('#twofactorcode_entry, [class^="login_AuthenticatorInputcontainer"] input.DialogInput, [class^="newlogindialog_SegmentedCharacterInput"] input, [class^="segmentedinputs_SegmentedCharacterInput"] input').length) { intersectionObserver.observe($J('#twofactorcode_entry, [class^="login_AuthenticatorInputcontainer"] input.DialogInput, [class^="newlogindialog_SegmentedCharacterInput"] input, [class^="segmentedinputs_SegmentedCharacterInput"] input')[0]); } if ($J('[class^="newlogindialog_EnterCodeInsteadLink"] [class^="newlogindialog_TextLink"]').length) { $J('[class^="newlogindialog_EnterCodeInsteadLink"] [class^="newlogindialog_TextLink"]')[0].click(); } }); var userSteamID; if ($J('#account_dropdown .persona').length) { if (typeof g_steamID != 'undefined' && g_steamID) { userSteamID = g_steamID; createConfirmationLink(userSteamID); } else { GM_xmlhttpRequest({ method: 'GET', url: 'https://steamcommunity.com/my/?xml=1', onload: function(response) { if (response.responseXML) { var steamID = $J(response.responseXML).find('steamID64').text(); if (steamID) { userSteamID = steamID; createConfirmationLink(userSteamID); } } } }); } if (window.location.href.indexOf('store.steampowered.com/login/?purchasetype=') !== -1) { mutationObserver.observe(document.body, {childList: true, subtree: true}); } } else { mutationObserver.observe(document.body, {childList: true, subtree: true}); } var timeOffset = 0; GM_xmlhttpRequest({ method: 'POST', url: 'https://api.steampowered.com/ITwoFactorService/QueryTime/v0001', responseType: 'json', onload: function(response) { if (response.response && response.response.response && response.response.response.server_time) { timeOffset = response.response.response.server_time - Math.floor(Date.now() / 1000); } } }); })();