// ==UserScript== // @name Steam lp // @namespace https://keylol.com/t652195-1-1 // @version 0.6 // @description steam lp // @author wave // @match http*://store.steampowered.com/* // @match http*://help.steampowered.com/* // @match http*://steamcommunity.com/* // @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 // @license MIT // @require https://cdn.jsdelivr.net/npm/crypto-js@4.0.0/crypto-js.min.js // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAFP0lEQVR4AdXXA5Ar2R7H8bMuPdtmnH/Gnr22bdu2bdu27bvewfO7tu2Mvw+pqene5EbTefj8ytUn9TtK0ip07p9tad5hTb2PK9xMdke7Y93pLypdaXii78KdZfmyirQL5m6r056ZsCO6OLFhoeLNMdNzfq4ihS+PnRH/yo74iZX0e3PbR2QlHsTUybIgQcRKt818RRnrYmKZSw4kyNho8vGTrynj3P91+fMOJITYaH3IsI3gy60Oa3dee/D+Fd+nwsLEScoYszpafHx82cfNjnRd2XFSx8md1jX6INlt9XomOj+rjCo998/K3f7i8pa/Oqez+ydK496vxk9LfuxEv0JNTxiwDTPGW764u0ee+Lzr55IrnnGgf/bDyqXe/2rnhZI4aPgpX3n9VX3/mv7ptjtU6RxPtCGapDy5Eaf82FZXfyTTn7p/qkqj/wJtAStD5wZasabHtNtgZmlNVRr1PxdKEl9w67cqgI3NzZoRdnovU+F78rUyL7QFGnwYxK35Sdor7ZhGp1X4ztmd2g0IcjYNT2oLVLqvwrenvFW3n3MaqCB0X6s9iElFpfiBnt7UoivwaZIKQsde2lFRXLKqcA3uoP0oJ+cdKgijW2tH2TjjUuHq3k0/l9umoL47m2jvgYnMWOXHm8oP3lAab6hwoN54I+wChW/kq5IUqODwRoHSjXtDhevM94+nnkopzofJwf3LufQd7ajjqU+/rv4P8UbW98c26tqlc7fSp3un6TXOf1mFYom5+l7BhMWQmDGRkFNvUcY3g5x774ZS6EQMjp3E69MsKrAh6Q4kInGSeOvU95R/vJt2WQg1iYxiH9lc5Y8cZBJpiM84qLEB/5dyVD0zElJiWchL9PLZQDLiIy4y/K9BrcWhbUBlLuDbLRohXjEzsKnyp+YxIfhU4h4ef2MaTahNYyaSicdLGvo4B6kDlD/VPxaCTRRnAMhlBC5Ek548BeAmSd7jjCswBYB8WiEIVZnLNpZQF0GowzMA1kSuQBQPAZiC4GQBRRTbQhRCXwDySI5UgXYAPCAKYSp6GxCEvwEwKlIFFgGwFiGBHPSKqIgwG4CtkSqwD4DxCB3xNg6hLQAfRKpANgBDEYbibRZCUwAyIlNgMh6zEWrjrTvCAACORKJAYwrxOIkg/AW920QjbAJgqfEFErhGsUKqI9ThCSXyaI2QyksAWhpdwMl+tDKIQqjCEfKAAj6knuapi4jRBbyP3AGiEYR4KlH8tbMCj55GFogihtq48XaW9ppfg6Zk4LEdCb1AXaIQr9RkI1cp4vXusodd7OIqxU4QFU6BPB6yjQGkIHgSzVpCVcg8XEg4BXLwKCCDedQljgw8LrKV1RzmBf49ZwvVECT8Alp5ANyns+YaLsSXx0xnII2JQxAjCmjl8inzKDkfAwC9zymHaGJgAa37rCMNQdhKiSKmIUgkC2hdJg6hKsWu0hgxrsAqLhPIBAThHgA7iUOMLCC4qMxkTpLD6yxHcHEOuIYgRhcoThz98W0QgosnwOeRLCBE8RK4zSROkqs57y6ERgCsimwBYScAPRAS6MR6jjEWz1X8AIAWRheos9uJaFKbIuApdRFdFgKQEcZLeu1eyp96E+2ILgsAyGEayQiCi3qcBOAV1UMuYGJOWeXPHrPNa9A2PPI4RzY38XhJWyTkpN07/57yhzdqbXN4DRvFU/Qywpi9YKFjVxXIX7+e8nfvCgmM5SAXuU0Wm2iJhBEbdXfxhgrsk+9U/MCCEzEwDqw0WcA7Kji8MaROtd3vP4xyiwGJcZe7WmPFshjfs/8Hvxyu95jGcTAAAAAASUVORK5CYII= // @downloadURL https://update.greasyfork.icu/scripts/440353/Steam%20lp.user.js // @updateURL https://update.greasyfork.icu/scripts/440353/Steam%20lp.meta.js // ==/UserScript== (function() { 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=android&t=' + time + '&p=' + encodeURIComponent(deviceID) + '&k=' + encodeURIComponent(key); } function showAddAccountDialog(strTitle, strOKButton, strCancelButton, rgModalParams) { if (!strOKButton) { strOKButton = '确定'; } if (!strCancelButton) { strCancelButton = '取消'; } var $Body = $J('
'); var $AccountNameInput = $J('', {type: 'text', 'class': ''}); var $SharedSecretInput = $J('', {type: 'text', 'class': ''}); var $SteamIDInput = $J('', {type: 'text', 'class': ''}); var $IdentitySecretInput = $J('', {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('
', {'class': 'newmodal_prompt_description'}).append('Steam 帐户名称 (?)')); $Body.append($J('
', {'class': 'newmodal_prompt_input gray_bevel for_text_input fullwidth'}).append($AccountNameInput)); $Body.append($J('
', {'class': 'newmodal_prompt_description', 'style': 'margin-top: 8px;'}).append('共享密钥 (?)')); $Body.append($J('
', {'class': 'newmodal_prompt_input gray_bevel for_text_input fullwidth'}).append($SharedSecretInput)); $Body.append($J('
', {'class': 'newmodal_prompt_description', 'style': 'margin-top: 8px;'}).append('64 位 Steam ID (?)')); $Body.append($J('
', {'class': 'newmodal_prompt_input gray_bevel for_text_input fullwidth'}).append($SteamIDInput)); $Body.append($J('
', {'class': 'newmodal_prompt_description', 'style': 'margin-top: 8px;'}).append('身份密钥 (?)')); $Body.append($J('
', {'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(''); var $TextArea = $J('