mirror of
https://github.com/DoctorMcKay/node-steamcommunity.git
synced 2026-08-19 21:23:28 +08:00
Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ec372b44b | ||
|
|
25dfa2cdd2 | ||
|
|
c006526f69 | ||
|
|
8e1f214612 | ||
|
|
38d3fb39bf | ||
|
|
e65d50b5f7 | ||
|
|
3a65768b3f | ||
|
|
96e3e7ebc0 | ||
|
|
e9bc778e2a | ||
|
|
c1901d5f55 | ||
|
|
028ee43bda | ||
|
|
0ba889ee7e | ||
|
|
ccf16b64e6 | ||
|
|
078e39584e | ||
|
|
2094f0545e | ||
|
|
b48e8fa537 | ||
|
|
2bfaecd88f | ||
|
|
f776d9fd4a | ||
|
|
86e87e88ed | ||
|
|
711ab8a3ae | ||
|
|
9218ec6dd9 | ||
|
|
d28c2cdde9 | ||
|
|
b98997a595 | ||
|
|
7ebbd5806f | ||
|
|
43c0504232 | ||
|
|
48f0637c18 | ||
|
|
ad095605b6 | ||
|
|
d09f18f055 | ||
|
|
c7a8c676b7 | ||
|
|
fd5ed31afa | ||
|
|
e2ef03cc32 | ||
|
|
e49c5c175e | ||
|
|
565132d1bc | ||
|
|
7c6e531dbd | ||
|
|
ac19e0867b | ||
|
|
e9d3221ee6 | ||
|
|
dfda22cee4 | ||
|
|
485529e807 | ||
|
|
3def387d56 | ||
|
|
605fb6268f | ||
|
|
89d058fc99 | ||
|
|
9d47d28a1e | ||
|
|
40ee35ac89 | ||
|
|
e252efc455 | ||
|
|
a4de664759 | ||
|
|
59df4dee1d | ||
|
|
ca887c46ba | ||
|
|
8c0994cec3 | ||
|
|
9ec329569f | ||
|
|
600a41143b | ||
|
|
4c59309eb4 | ||
|
|
2adc23f0ac | ||
|
|
5b40683873 | ||
|
|
63b0b82531 | ||
|
|
2b678bea06 | ||
|
|
8ffa0b50d4 | ||
|
|
d093466d27 | ||
|
|
c69a5451eb | ||
|
|
9a4be453ec | ||
|
|
c550b380fc | ||
|
|
247d804c77 | ||
|
|
dfd0ccbbc7 | ||
|
|
fa253920e3 | ||
|
|
30ebf333b4 | ||
|
|
d7cfc396a7 | ||
|
|
ac8e4a62de | ||
|
|
8e9e3723f2 | ||
|
|
6b0f958cfd | ||
|
|
8a7d6f5018 | ||
|
|
0cb0d22433 | ||
|
|
103e941501 | ||
|
|
f99d86aa73 | ||
|
|
5a855316f6 | ||
|
|
729f078a25 | ||
|
|
80dbf9d688 |
50
.eslintrc.js
Normal file
50
.eslintrc.js
Normal file
@@ -0,0 +1,50 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
commonjs: true,
|
||||
es2021: true,
|
||||
node: true
|
||||
},
|
||||
extends: 'eslint:recommended',
|
||||
parserOptions: {
|
||||
ecmaVersion: 12
|
||||
},
|
||||
rules: {
|
||||
// Use tabs for indentation and require 'case' in switch to be indented 1 level (default 0)
|
||||
indent: ['error', 'tab', {SwitchCase: 1}],
|
||||
// Single quotes for strings
|
||||
quotes: ['error', 'single'],
|
||||
// Always require semicolons
|
||||
semi: ['error', 'always'],
|
||||
// Don't use 'var'
|
||||
'no-var': 'error',
|
||||
// Only use quotes in object literal keys as needed
|
||||
'quote-props': ['error', 'as-needed'],
|
||||
// Don't allow trailing spaces after a line
|
||||
'no-trailing-spaces': 'error',
|
||||
// Require spaces before and after keywords (like "if")
|
||||
'keyword-spacing': 'error',
|
||||
// Don't allow unused variables, but allow unused function args (e.g. in callbacks) and global vars
|
||||
'no-unused-vars': ['error', {vars: 'local', args: 'none', varsIgnorePattern: '^_'}],
|
||||
// Require using dot notation (obj.prop instead of obj['prop']) where possible
|
||||
'dot-notation': 'error',
|
||||
// Don't use spaces before parens in anonymous or named functions
|
||||
'space-before-function-paren': ['error', {anonymous: 'never', named: 'never', asyncArrow: 'always'}]
|
||||
|
||||
// We will NOT be using eqeqeq for a few reasons:
|
||||
// 1. I would have to go through and check every single `==` to make sure that it's not depending on loose equality checks.
|
||||
// 2. I'm only using ESLint to enforce style, not actual differences in functionality. ==/=== is not merely a style choice.
|
||||
// Yes, I know that 'no-var' is actually enforcing a difference in functionality, but in practice nobody uses
|
||||
// (or even knows about) var's hoisting functionality, so at this point it's effectively a style choice.
|
||||
// 3. A lot of the time, you actually *want* loose equality checks, especially when interacting with a web server
|
||||
// (as HTTP as no concept of anything but strings). Yes, most of our interaction is JSON, but not all. And even then,
|
||||
// not all JSON actually serializes numbers as numbers.
|
||||
// 4. `==` is really nowhere near as dangerous as memes would lead you to believe, if you know what you're doing.
|
||||
// 5. If the idea behind enforcing `===` is to prevent inexperienced developers from unwittingly introducing bugs
|
||||
// via loose quality checks, in my opinion it could be just as harmful to instruct a code quality tool to
|
||||
// naively demand that all `==` become `===`. If a developer were to build code that works, but upon opening
|
||||
// a pull request they see that ESLint demands they use `===` instead, they might just click "fix" and resubmit,
|
||||
// expecting the code quality tool to know what it's doing. But it *doesn't* know what it's doing, since it's
|
||||
// just blindly alerting when it sees `==`. The change in functionality from `==` to `===` could very well
|
||||
// introduce a bug by itself.
|
||||
}
|
||||
};
|
||||
31
.github/workflows/eslint.yml
vendored
Normal file
31
.github/workflows/eslint.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: ESLint
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Run for external PRs, but not on our own internal PRs as they'll be run by the push to the branch.
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'DoctorMcKay/node-steamcommunity'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: npm install
|
||||
working-directory: .
|
||||
run: npm install --ignore-scripts
|
||||
|
||||
- name: Run ESLint
|
||||
run: npm run lint
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
node_modules/
|
||||
node_modules/*
|
||||
test.js
|
||||
dev/
|
||||
|
||||
|
||||
42
.idea/codeStyles/Project.xml
generated
42
.idea/codeStyles/Project.xml
generated
@@ -1,15 +1,45 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<codeStyleSettings language="JSON">
|
||||
<indentOptions>
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<codeStyleSettings language="CSS">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="INDENT_CASE_FROM_SWITCH" value="false" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="ALIGN_MULTILINE_FOR" value="false" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
</component>
|
||||
|
||||
2
.idea/codeStyles/codeStyleConfig.xml
generated
2
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -3,4 +3,4 @@
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
</component>
|
||||
|
||||
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -4,8 +4,10 @@
|
||||
<inspection_tool class="ES6ConvertRequireIntoImport" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="ES6ConvertToForOf" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="ES6ConvertVarToLetConst" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JSEqualityComparisonWithCoercion" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSFunctionExpressionToArrowFunction" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="JSStringConcatenationToES6Template" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="JSUnfilteredForInLoop" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
||||
@@ -4,8 +4,9 @@
|
||||
[](https://github.com/DoctorMcKay/node-steamcommunity/blob/master/LICENSE)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=N36YVAT42CZ4G&item_name=node%2dsteamcommunity¤cy_code=USD)
|
||||
|
||||
This module provides an easy interface for the Steam Community website. This module can be used to simply login to
|
||||
steamcommunity.com for use with other libraries, or to interact with steamcommunity.com.
|
||||
This module provides an easy interface for the Steam Community website. This module can be used to simply login to steamcommunity.com for use with other libraries, or to interact with steamcommunity.com.
|
||||
|
||||
It supports Steam Guard and CAPTCHAs.
|
||||
|
||||
**Have a question about the module or coding in general? *Do not create a GitHub issue.* GitHub issues are for feature
|
||||
requests and bug reports. Instead, post in the [dedicated forum](https://dev.doctormckay.com/forum/8-node-steamcommunity/).
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
const StdLib = require('@doctormckay/stdlib');
|
||||
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
module.exports = CConfirmation;
|
||||
|
||||
@@ -18,20 +20,33 @@ function CConfirmation(community, data) {
|
||||
this.offerID = this.type == SteamCommunity.ConfirmationType.Trade ? this.creator : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} time
|
||||
* @param {string} key
|
||||
* @param {function} [callback]
|
||||
* @return Promise<{offerID: number}>
|
||||
*/
|
||||
CConfirmation.prototype.getOfferID = function(time, key, callback) {
|
||||
if (this.type && this.creator) {
|
||||
if (this.type != SteamCommunity.ConfirmationType.Trade) {
|
||||
callback(new Error('Not a trade confirmation'));
|
||||
return;
|
||||
return StdLib.Promises.callbackPromise(['offerID'], null, false, async (resolve, reject) => {
|
||||
if (this.type && this.creator) {
|
||||
if (this.type != SteamCommunity.ConfirmationType.Trade) {
|
||||
return reject(new Error('Not a trade confirmation'));
|
||||
}
|
||||
|
||||
return resolve({offerID: this.creator});
|
||||
}
|
||||
|
||||
callback(null, this.creator);
|
||||
return;
|
||||
}
|
||||
|
||||
this._community.getConfirmationOfferID(this.id, time, key, callback);
|
||||
return await this._community.getConfirmationOfferID(this.id, time, key, callback);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} time
|
||||
* @param {string} key
|
||||
* @param {boolean} accept
|
||||
* @param {function} [callback]
|
||||
* @return Promise<void>
|
||||
*/
|
||||
CConfirmation.prototype.respond = function(time, key, accept, callback) {
|
||||
this._community.respondToConfirmation(this.id, this.key, time, key, accept, callback);
|
||||
return this._community.respondToConfirmation(this.id, this.key, time, key, accept, callback);
|
||||
};
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
module.exports = CEconItem;
|
||||
|
||||
function CEconItem(item, description, contextID) {
|
||||
var thing;
|
||||
for (thing in item) {
|
||||
if (item.hasOwnProperty(thing)) {
|
||||
this[thing] = item[thing];
|
||||
}
|
||||
for (let thing in item) {
|
||||
this[thing] = item[thing];
|
||||
}
|
||||
|
||||
var isCurrency = !!(this.is_currency || this.currency) || typeof this.currencyid !== 'undefined'; // I don't want to put this on the object yet; it's nice to have the ids at the top of printed output
|
||||
let isCurrency = !!(this.is_currency || this.currency) || typeof this.currencyid !== 'undefined'; // I don't want to put this on the object yet; it's nice to have the ids at the top of printed output
|
||||
|
||||
if (isCurrency) {
|
||||
this.currencyid = this.id = (this.id || this.currencyid);
|
||||
@@ -27,10 +24,8 @@ function CEconItem(item, description, contextID) {
|
||||
description = description[this.classid + '_' + this.instanceid];
|
||||
}
|
||||
|
||||
for (thing in description) {
|
||||
if (description.hasOwnProperty(thing)) {
|
||||
this[thing] = description[thing];
|
||||
}
|
||||
for (let thing in description) {
|
||||
this[thing] = description[thing];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,28 +44,26 @@ function CEconItem(item, description, contextID) {
|
||||
|
||||
// Restore old property names of tags
|
||||
if (this.tags) {
|
||||
this.tags = this.tags.map(function(tag) {
|
||||
return {
|
||||
"internal_name": tag.internal_name,
|
||||
"name": tag.localized_tag_name || tag.name,
|
||||
"category": tag.category,
|
||||
"color": tag.color || "",
|
||||
"category_name": tag.localized_category_name || tag.category_name
|
||||
};
|
||||
});
|
||||
this.tags = this.tags.map((tag) => ({
|
||||
internal_name: tag.internal_name,
|
||||
name: tag.localized_tag_name || tag.name,
|
||||
category: tag.category,
|
||||
color: tag.color || '',
|
||||
category_name: tag.localized_category_name || tag.category_name
|
||||
}));
|
||||
}
|
||||
|
||||
// Restore market_fee_app, if applicable
|
||||
var match;
|
||||
if (this.appid == 753 && this.contextid == 6 && this.market_hash_name && (match = this.market_hash_name.match(/^(\d+)\-/))) {
|
||||
let match;
|
||||
if (this.appid == 753 && this.contextid == 6 && this.market_hash_name && (match = this.market_hash_name.match(/^(\d+)-/))) {
|
||||
this.market_fee_app = parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
// Restore cache_expiration, if we can (for CS:GO items)
|
||||
if (this.appid == 730 && this.contextid == 2 && this.owner_descriptions) {
|
||||
let description = this.owner_descriptions.find(d => d.value && d.value.indexOf('Tradable/Marketable After ') == 0);
|
||||
let description = this.owner_descriptions.find(d => d.value && d.value.indexOf('Tradable After ') == 0);
|
||||
if (description) {
|
||||
let date = new Date(description.value.substring(26).replace(/[,()]/g, ''));
|
||||
let date = new Date(description.value.substring(15).replace(/[,()]/g, ''));
|
||||
if (date) {
|
||||
this.cache_expiration = date.toISOString();
|
||||
}
|
||||
@@ -82,7 +75,7 @@ function CEconItem(item, description, contextID) {
|
||||
this.cache_expiration = this.item_expiration;
|
||||
}
|
||||
|
||||
if (this.actions === "") {
|
||||
if (this.actions === '') {
|
||||
this.actions = [];
|
||||
}
|
||||
|
||||
@@ -94,15 +87,15 @@ function CEconItem(item, description, contextID) {
|
||||
}
|
||||
|
||||
CEconItem.prototype.getImageURL = function() {
|
||||
return "https://steamcommunity-a.akamaihd.net/economy/image/" + this.icon_url + "/";
|
||||
return 'https://steamcommunity-a.akamaihd.net/economy/image/' + this.icon_url + '/';
|
||||
};
|
||||
|
||||
CEconItem.prototype.getLargeImageURL = function() {
|
||||
if(!this.icon_url_large) {
|
||||
if (!this.icon_url_large) {
|
||||
return this.getImageURL();
|
||||
}
|
||||
|
||||
return "https://steamcommunity-a.akamaihd.net/economy/image/" + this.icon_url_large + "/";
|
||||
return 'https://steamcommunity-a.akamaihd.net/economy/image/' + this.icon_url_large + '/';
|
||||
};
|
||||
|
||||
CEconItem.prototype.getTag = function(category) {
|
||||
@@ -110,7 +103,7 @@ CEconItem.prototype.getTag = function(category) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.tags.length; i++) {
|
||||
for (let i = 0; i < this.tags.length; i++) {
|
||||
if (this.tags[i].category == category) {
|
||||
return this.tags[i];
|
||||
}
|
||||
|
||||
@@ -1,33 +1,35 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var Cheerio = require('cheerio');
|
||||
const Cheerio = require('cheerio');
|
||||
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
SteamCommunity.prototype.getMarketItem = function(appid, hashName, currency, callback) {
|
||||
if (typeof currency == "function") {
|
||||
if (typeof currency == 'function') {
|
||||
callback = currency;
|
||||
currency = 1;
|
||||
}
|
||||
var self = this;
|
||||
this.httpRequest("https://steamcommunity.com/market/listings/" + appid + "/" + encodeURIComponent(hashName), function(err, response, body) {
|
||||
|
||||
this.httpRequest('https://steamcommunity.com/market/listings/' + appid + '/' + encodeURIComponent(hashName), (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body);
|
||||
if($('.market_listing_table_message') && $('.market_listing_table_message').text().trim() == 'There are no listings for this item.') {
|
||||
callback(new Error("There are no listings for this item."));
|
||||
let $ = Cheerio.load(body);
|
||||
let $listingTableMessage = $('.market_listing_table_message');
|
||||
if ($listingTableMessage && $listingTableMessage.text().trim() == 'There are no listings for this item.') {
|
||||
callback(new Error('There are no listings for this item.'));
|
||||
return;
|
||||
}
|
||||
|
||||
var item = new CMarketItem(appid, hashName, self, body, $);
|
||||
item.updatePrice(currency, function(err) {
|
||||
if(err) {
|
||||
let item = new CMarketItem(appid, hashName, this, body, $);
|
||||
item.updatePrice(currency, (err) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, item);
|
||||
}
|
||||
});
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
function CMarketItem(appid, hashName, community, body, $) {
|
||||
@@ -36,38 +38,36 @@ function CMarketItem(appid, hashName, community, body, $) {
|
||||
this._community = community;
|
||||
this._$ = $;
|
||||
|
||||
this._country = "US";
|
||||
var match = body.match(/var g_strCountryCode = "([^"]+)";/);
|
||||
if(match) {
|
||||
this._country = 'US';
|
||||
let match = body.match(/var g_strCountryCode = "([^"]+)";/);
|
||||
if (match) {
|
||||
this._country = match[1];
|
||||
}
|
||||
|
||||
this._language = "english";
|
||||
this._language = 'english';
|
||||
match = body.match(/var g_strLanguage = "([^"]+)";/);
|
||||
if(match) {
|
||||
if (match) {
|
||||
this._language = match[1];
|
||||
}
|
||||
|
||||
this.commodity = false;
|
||||
match = body.match(/Market_LoadOrderSpread\(\s*(\d+)\s*\);/);
|
||||
if(match) {
|
||||
if (match) {
|
||||
this.commodity = true;
|
||||
this.commodityID = parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
this.medianSalePrices = null;
|
||||
match = body.match(/var line1=([^;]+);/);
|
||||
if(match) {
|
||||
if (match) {
|
||||
try {
|
||||
this.medianSalePrices = JSON.parse(match[1]);
|
||||
this.medianSalePrices = this.medianSalePrices.map(function(item) {
|
||||
return {
|
||||
"hour": new Date(item[0]),
|
||||
"price": item[1],
|
||||
"quantity": parseInt(item[2], 10)
|
||||
};
|
||||
});
|
||||
} catch(e) {
|
||||
this.medianSalePrices = this.medianSalePrices.map((item) => ({
|
||||
hour: new Date(item[0]),
|
||||
price: item[1],
|
||||
quantity: parseInt(item[2], 10)
|
||||
}));
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ function CMarketItem(appid, hashName, community, body, $) {
|
||||
// TODO: Buying listings and placing buy orders
|
||||
}
|
||||
|
||||
CMarketItem.prototype.updatePrice = function (currency, callback) {
|
||||
CMarketItem.prototype.updatePrice = function(currency, callback) {
|
||||
if (this.commodity) {
|
||||
this.updatePriceForCommodity(currency, callback);
|
||||
} else {
|
||||
@@ -100,90 +100,88 @@ CMarketItem.prototype.updatePrice = function (currency, callback) {
|
||||
};
|
||||
|
||||
CMarketItem.prototype.updatePriceForCommodity = function(currency, callback) {
|
||||
if(!this.commodity) {
|
||||
throw new Error("Cannot update price for non-commodity item");
|
||||
if (!this.commodity) {
|
||||
throw new Error('Cannot update price for non-commodity item');
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this._community.httpRequest({
|
||||
"uri": "https://steamcommunity.com/market/itemordershistogram?country=US&language=english¤cy=" + currency + "&item_nameid=" + this.commodityID,
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.success != 1) {
|
||||
if(callback) {
|
||||
callback(new Error("Error " + body.success));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var match = (body.sell_order_summary || '').match(/<span class="market_commodity_orders_header_promote">(\d+)<\/span>/);
|
||||
if(match) {
|
||||
self.quantity = parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
self.buyQuantity = 0;
|
||||
match = (body.buy_order_summary || '').match(/<span class="market_commodity_orders_header_promote">(\d+)<\/span>/);
|
||||
if(match) {
|
||||
self.buyQuantity = parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
self.lowestPrice = parseInt(body.lowest_sell_order, 10);
|
||||
self.highestBuyOrder = parseInt(body.highest_buy_order, 10);
|
||||
|
||||
// TODO: The tables?
|
||||
if(callback) {
|
||||
callback(null);
|
||||
}
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
CMarketItem.prototype.updatePriceForNonCommodity = function (currency, callback) {
|
||||
if(this.commodity) {
|
||||
throw new Error("Cannot update price for commodity item");
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this._community.httpRequest({
|
||||
"uri": "https://steamcommunity.com/market/listings/" +
|
||||
this._appid + "/" +
|
||||
encodeURIComponent(this._hashName) +
|
||||
"/render/?query=&start=0&count=10&country=US&language=english¤cy=" + currency,
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
url: 'https://steamcommunity.com/market/itemordershistogram?country=US&language=english¤cy=' + currency + '&item_nameid=' + this.commodityID,
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success != 1) {
|
||||
callback && callback(new Error("Error " + body.success));
|
||||
if (callback) {
|
||||
callback(new Error('Error ' + body.success));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var match = body.total_count;
|
||||
let match = (body.sell_order_summary || '').match(/<span class="market_commodity_orders_header_promote">(\d+)<\/span>/);
|
||||
if (match) {
|
||||
self.quantity = parseInt(match, 10);
|
||||
this.quantity = parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
var lowestPrice;
|
||||
var $ = Cheerio.load(body.results_html);
|
||||
match = $(".market_listing_price.market_listing_price_with_fee");
|
||||
this.buyQuantity = 0;
|
||||
match = (body.buy_order_summary || '').match(/<span class="market_commodity_orders_header_promote">(\d+)<\/span>/);
|
||||
if (match) {
|
||||
for (var i = 0; i < match.length; i++) {
|
||||
lowestPrice = parseFloat($(match[i]).text().replace(",", ".").replace(/[^\d.]/g, ''));
|
||||
this.buyQuantity = parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
this.lowestPrice = parseInt(body.lowest_sell_order, 10);
|
||||
this.highestBuyOrder = parseInt(body.highest_buy_order, 10);
|
||||
|
||||
// TODO: The tables?
|
||||
if (callback) {
|
||||
callback(null);
|
||||
}
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
CMarketItem.prototype.updatePriceForNonCommodity = function(currency, callback) {
|
||||
if (this.commodity) {
|
||||
throw new Error('Cannot update price for commodity item');
|
||||
}
|
||||
|
||||
this._community.httpRequest({
|
||||
url: 'https://steamcommunity.com/market/listings/' +
|
||||
this._appid + '/' +
|
||||
encodeURIComponent(this._hashName) +
|
||||
'/render/?query=&start=0&count=10&country=US&language=english¤cy=' + currency,
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success != 1) {
|
||||
callback && callback(new Error('Error ' + body.success));
|
||||
return;
|
||||
}
|
||||
|
||||
let match = body.total_count;
|
||||
if (match) {
|
||||
this.quantity = parseInt(match, 10);
|
||||
}
|
||||
|
||||
let lowestPrice;
|
||||
let $ = Cheerio.load(body.results_html);
|
||||
match = $('.market_listing_price.market_listing_price_with_fee');
|
||||
if (match) {
|
||||
for (let i = 0; i < match.length; i++) {
|
||||
lowestPrice = parseFloat($(match[i]).text().replace(',', '.').replace(/[^\d.]/g, ''));
|
||||
if (!isNaN(lowestPrice)) {
|
||||
self.lowestPrice = lowestPrice;
|
||||
this.lowestPrice = lowestPrice;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback && callback(null);
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var Cheerio = require('cheerio');
|
||||
const Cheerio = require('cheerio');
|
||||
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
SteamCommunity.prototype.marketSearch = function(options, callback) {
|
||||
var qs = {};
|
||||
let qs = {};
|
||||
|
||||
if(typeof options === 'string') {
|
||||
if (typeof options === 'string') {
|
||||
qs.query = options;
|
||||
} else {
|
||||
qs.query = options.query || '';
|
||||
qs.appid = options.appid;
|
||||
qs.search_descriptions = options.searchDescriptions ? 1 : 0;
|
||||
|
||||
if(qs.appid) {
|
||||
for(var i in options) {
|
||||
if(['query', 'appid', 'searchDescriptions'].indexOf(i) != -1) {
|
||||
if (qs.appid) {
|
||||
for (let i in options) {
|
||||
if (['query', 'appid', 'searchDescriptions'].indexOf(i) != -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -28,62 +29,61 @@ SteamCommunity.prototype.marketSearch = function(options, callback) {
|
||||
qs.sort_column = 'price';
|
||||
qs.sort_dir = 'asc';
|
||||
|
||||
var self = this;
|
||||
var results = [];
|
||||
performSearch();
|
||||
|
||||
function performSearch() {
|
||||
self.httpRequest({
|
||||
"uri": "https://steamcommunity.com/market/search/render/",
|
||||
"qs": qs,
|
||||
"headers": {
|
||||
"referer": "https://steamcommunity.com/market/search"
|
||||
let results = [];
|
||||
const performSearch = () => {
|
||||
this.httpRequest({
|
||||
url: 'https://steamcommunity.com/market/search/render/',
|
||||
qs: qs,
|
||||
headers: {
|
||||
referer: 'https://steamcommunity.com/market/search'
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body.success) {
|
||||
callback(new Error("Success is not true"));
|
||||
if (!body.success) {
|
||||
callback(new Error('Success is not true'));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body.results_html) {
|
||||
callback(new Error("No results_html in response"));
|
||||
if (!body.results_html) {
|
||||
callback(new Error('No results_html in response'));
|
||||
return;
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body.results_html);
|
||||
var $errorMsg = $('.market_listing_table_message');
|
||||
if($errorMsg.length > 0) {
|
||||
let $ = Cheerio.load(body.results_html);
|
||||
let $errorMsg = $('.market_listing_table_message');
|
||||
if ($errorMsg.length > 0) {
|
||||
callback(new Error($errorMsg.text()));
|
||||
return;
|
||||
}
|
||||
|
||||
var rows = $('.market_listing_row_link');
|
||||
for(var i = 0; i < rows.length; i++) {
|
||||
let rows = $('.market_listing_row_link');
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
results.push(new CMarketSearchResult($(rows[i])));
|
||||
}
|
||||
|
||||
if(body.start + body.pagesize >= body.total_count) {
|
||||
if (body.start + body.pagesize >= body.total_count) {
|
||||
callback(null, results);
|
||||
} else {
|
||||
qs.start += body.pagesize;
|
||||
performSearch();
|
||||
}
|
||||
}, "steamcommunity");
|
||||
}
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
performSearch();
|
||||
};
|
||||
|
||||
function CMarketSearchResult(row) {
|
||||
var match = row.attr('href').match(/\/market\/listings\/(\d+)\/([^\?\/]+)/);
|
||||
let match = row.attr('href').match(/\/market\/listings\/(\d+)\/([^?/]+)/);
|
||||
|
||||
this.appid = parseInt(match[1], 10);
|
||||
this.market_hash_name = decodeURIComponent(match[2]);
|
||||
this.image = ((row.find('.market_listing_item_img').attr('src') || "").match(/^https?:\/\/[^\/]+\/economy\/image\/[^\/]+\//) || [])[0];
|
||||
this.image = ((row.find('.market_listing_item_img').attr('src') || '').match(/^https?:\/\/[^/]+\/economy\/image\/[^/]+\//) || [])[0];
|
||||
this.price = parseInt(row.find('.market_listing_their_price .market_table_value span.normal_price').text().replace(/[^\d]+/g, ''), 10);
|
||||
this.quantity = parseInt(row.find('.market_listing_num_listings_qty').text().replace(/[^\d]+/g, ''), 10);
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var Helpers = require('../components/helpers.js');
|
||||
var SteamID = require('steamid');
|
||||
var xml2js = require('xml2js');
|
||||
const SteamID = require('steamid');
|
||||
const XML2JS = require('xml2js');
|
||||
|
||||
const Helpers = require('../components/helpers.js');
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
SteamCommunity.prototype.getSteamGroup = function(id, callback) {
|
||||
if(typeof id !== 'string' && !Helpers.isSteamID(id)) {
|
||||
throw new Error("id parameter should be a group URL string or a SteamID object");
|
||||
if (typeof id !== 'string' && !Helpers.isSteamID(id)) {
|
||||
throw new Error('id parameter should be a group URL string or a SteamID object');
|
||||
}
|
||||
|
||||
if(typeof id === 'object' && (id.universe != SteamID.Universe.PUBLIC || id.type != SteamID.Type.CLAN)) {
|
||||
throw new Error("SteamID must stand for a clan account in the public universe");
|
||||
if (typeof id === 'object' && (id.universe != SteamID.Universe.PUBLIC || id.type != SteamID.Type.CLAN)) {
|
||||
throw new Error('SteamID must stand for a clan account in the public universe');
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequest("https://steamcommunity.com/" + (typeof id === 'string' ? "groups/" + id : "gid/" + id.toString()) + "/memberslistxml/?xml=1", function(err, response, body) {
|
||||
this.httpRequest('https://steamcommunity.com/' + (typeof id === 'string' ? 'groups/' + id : 'gid/' + id.toString()) + '/memberslistxml/?xml=1', (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
xml2js.parseString(body, function(err, result) {
|
||||
if(err) {
|
||||
XML2JS.parseString(body, (err, result) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, new CSteamGroup(self, result.memberList));
|
||||
callback(null, new CSteamGroup(this, result.memberList));
|
||||
});
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
function CSteamGroup(community, groupData) {
|
||||
@@ -49,16 +49,16 @@ CSteamGroup.prototype.getAvatarURL = function(size, protocol) {
|
||||
size = size || '';
|
||||
protocol = protocol || 'http://';
|
||||
|
||||
var url = protocol + "steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/" + this.avatarHash.substring(0, 2) + "/" + this.avatarHash;
|
||||
if(size == 'full' || size == 'medium') {
|
||||
return url + "_" + size + ".jpg";
|
||||
let url = protocol + 'steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/' + this.avatarHash.substring(0, 2) + '/' + this.avatarHash;
|
||||
if (size == 'full' || size == 'medium') {
|
||||
return url + '_' + size + '.jpg';
|
||||
} else {
|
||||
return url + ".jpg";
|
||||
return url + '.jpg';
|
||||
}
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.getMembers = function(addresses, callback) {
|
||||
if(typeof addresses === 'function') {
|
||||
if (typeof addresses === 'function') {
|
||||
callback = addresses;
|
||||
addresses = null;
|
||||
}
|
||||
@@ -83,11 +83,11 @@ CSteamGroup.prototype.postAnnouncement = function(headline, content, hidden, cal
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.editAnnouncement = function(annoucementID, headline, content, callback) {
|
||||
this._community.editGroupAnnouncement(this.steamID, annoucementID, headline, content, callback)
|
||||
this._community.editGroupAnnouncement(this.steamID, annoucementID, headline, content, callback);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.deleteAnnouncement = function(annoucementID, callback) {
|
||||
this._community.deleteGroupAnnouncement(this.steamID, annoucementID, callback)
|
||||
this._community.deleteGroupAnnouncement(this.steamID, annoucementID, callback);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.scheduleEvent = function(name, type, description, time, server, callback) {
|
||||
@@ -98,7 +98,7 @@ CSteamGroup.prototype.editEvent = function(id, name, type, description, time, se
|
||||
this._community.editGroupEvent(this.steamID, id, name, type, description, time, server, callback);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.deleteEvent = function (id, callback) {
|
||||
CSteamGroup.prototype.deleteEvent = function(id, callback) {
|
||||
this._community.deleteGroupEvent(this.steamID, id, callback);
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) {
|
||||
};
|
||||
|
||||
// Get DOM of sharedfile
|
||||
this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sharedFileId}`, (err, res, body) => {
|
||||
this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sharedFileId}`, async (err, res, body) => {
|
||||
try {
|
||||
|
||||
/* --------------------- Preprocess output --------------------- */
|
||||
@@ -136,18 +136,10 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) {
|
||||
// Find owner profile link, convert to steamID64 using SteamIdResolver lib and create a SteamID object
|
||||
let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
|
||||
|
||||
Helpers.resolveVanityURL(ownerHref, (err, data) => { // This request takes <1 sec
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
sharedfile.owner = new SteamID(data.steamID);
|
||||
|
||||
// Make callback when ID was resolved as otherwise owner will always be null
|
||||
callback(null, new CSteamSharedFile(this, sharedfile));
|
||||
});
|
||||
let {steamID} = await this._resolveVanityURL(ownerHref);
|
||||
sharedfile.owner = steamID;
|
||||
|
||||
callback(null, new CSteamSharedFile(this, sharedfile));
|
||||
} catch (err) {
|
||||
callback(err, null);
|
||||
}
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var Helpers = require('../components/helpers.js');
|
||||
var SteamID = require('steamid');
|
||||
var xml2js = require('xml2js');
|
||||
const SteamID = require('steamid');
|
||||
const XML2JS = require('xml2js');
|
||||
|
||||
const Helpers = require('../components/helpers.js');
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
SteamCommunity.prototype.getSteamUser = function(id, callback) {
|
||||
if(typeof id !== 'string' && !Helpers.isSteamID(id)) {
|
||||
throw new Error("id parameter should be a user URL string or a SteamID object");
|
||||
if (typeof id !== 'string' && !Helpers.isSteamID(id)) {
|
||||
throw new Error('id parameter should be a user URL string or a SteamID object');
|
||||
}
|
||||
|
||||
if(typeof id === 'object' && (id.universe != SteamID.Universe.PUBLIC || id.type != SteamID.Type.INDIVIDUAL)) {
|
||||
throw new Error("SteamID must stand for an individual account in the public universe");
|
||||
if (typeof id === 'object' && (id.universe != SteamID.Universe.PUBLIC || id.type != SteamID.Type.INDIVIDUAL)) {
|
||||
throw new Error('SteamID must stand for an individual account in the public universe');
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequest("https://steamcommunity.com/" + (typeof id === 'string' ? "id/" + id : "profiles/" + id.toString()) + "/?xml=1", function(err, response, body) {
|
||||
this.httpRequest('http://steamcommunity.com/' + (typeof id === 'string' ? 'id/' + id : 'profiles/' + id.toString()) + '/?xml=1', (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
xml2js.parseString(body, function(err, result) {
|
||||
if(err || (!result.response && !result.profile)) {
|
||||
callback(err || new Error("No valid response"));
|
||||
XML2JS.parseString(body, (err, result) => {
|
||||
if (err || (!result.response && !result.profile)) {
|
||||
callback(err || new Error('No valid response'));
|
||||
return;
|
||||
}
|
||||
|
||||
if(result.response && result.response.error && result.response.error.length) {
|
||||
if (result.response && result.response.error && result.response.error.length) {
|
||||
callback(new Error(result.response.error[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
// Try and find custom URL from redirect
|
||||
var customurl = null;
|
||||
if(response.request.redirects && response.request.redirects.length) {
|
||||
var match = response.request.redirects[0].redirectUri.match(/https?:\/\/steamcommunity\.com\/id\/([^/])+\/\?xml=1/);
|
||||
if(match) {
|
||||
let customurl = null;
|
||||
if (response.request.redirects && response.request.redirects.length) {
|
||||
let match = response.request.redirects[0].redirectUri.match(/https?:\/\/steamcommunity\.com\/id\/([^/])+\/\?xml=1/);
|
||||
if (match) {
|
||||
customurl = match[1];
|
||||
}
|
||||
}
|
||||
|
||||
if(!result.profile.steamID64) {
|
||||
callback(new Error("No valid response"));
|
||||
if (!result.profile.steamID64) {
|
||||
callback(new Error('No valid response'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, new CSteamUser(self, result.profile, customurl));
|
||||
callback(null, new CSteamUser(this, result.profile, customurl));
|
||||
});
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
function CSteamUser(community, userData, customurl) {
|
||||
@@ -59,7 +59,7 @@ function CSteamUser(community, userData, customurl) {
|
||||
this.privacyState = processItem('privacyState', 'uncreated');
|
||||
this.visibilityState = processItem('visibilityState');
|
||||
this.avatarHash = processItem('avatarIcon', '').match(/([0-9a-f]+)\.[a-z]+$/);
|
||||
if(this.avatarHash) {
|
||||
if (this.avatarHash) {
|
||||
this.avatarHash = this.avatarHash[1];
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ function CSteamUser(community, userData, customurl) {
|
||||
this.isLimitedAccount = processItem('isLimitedAccount') == 1;
|
||||
this.customURL = processItem('customURL', customurl);
|
||||
|
||||
if(this.visibilityState == 3) {
|
||||
let memberSinceValue = processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, "$1");
|
||||
if (this.visibilityState == SteamCommunity.PrivacyState.Public) {
|
||||
let memberSinceValue = processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, '$1');
|
||||
|
||||
if (memberSinceValue.indexOf(',') === -1) {
|
||||
memberSinceValue += ', ' + new Date().getFullYear();
|
||||
@@ -91,11 +91,10 @@ function CSteamUser(community, userData, customurl) {
|
||||
this.groups = null;
|
||||
this.primaryGroup = null;
|
||||
|
||||
var self = this;
|
||||
if(userData.groups && userData.groups[0] && userData.groups[0].group) {
|
||||
this.groups = userData.groups[0].group.map(function(group) {
|
||||
if(group['$'] && group['$'].isPrimary === "1") {
|
||||
self.primaryGroup = new SteamID(group.groupID64[0]);
|
||||
if (userData.groups && userData.groups[0] && userData.groups[0].group) {
|
||||
this.groups = userData.groups[0].group.map((group) => {
|
||||
if (group.$ && group.$.isPrimary === '1') {
|
||||
this.primaryGroup = new SteamID(group.groupID64[0]);
|
||||
}
|
||||
|
||||
return new SteamID(group.groupID64[0]);
|
||||
@@ -103,7 +102,7 @@ function CSteamUser(community, userData, customurl) {
|
||||
}
|
||||
|
||||
function processItem(name, defaultVal) {
|
||||
if(!userData[name]) {
|
||||
if (!userData[name]) {
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
@@ -115,13 +114,13 @@ CSteamUser.getAvatarURL = function(hash, size, protocol) {
|
||||
size = size || '';
|
||||
protocol = protocol || 'http://';
|
||||
|
||||
hash = hash || "72f78b4c8cc1f62323f8a33f6d53e27db57c2252"; // The default "?" avatar
|
||||
hash = hash || '72f78b4c8cc1f62323f8a33f6d53e27db57c2252'; // The default "?" avatar
|
||||
|
||||
var url = protocol + "steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/" + hash.substring(0, 2) + "/" + hash;
|
||||
if(size == 'full' || size == 'medium') {
|
||||
return url + "_" + size + ".jpg";
|
||||
let url = protocol + 'steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/' + hash.substring(0, 2) + '/' + hash;
|
||||
if (size == 'full' || size == 'medium') {
|
||||
return url + '_' + size + '.jpg';
|
||||
} else {
|
||||
return url + ".jpg";
|
||||
return url + '.jpg';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,14 +165,6 @@ CSteamUser.prototype.inviteToGroup = function(groupID, callback) {
|
||||
this._community.inviteUserToGroup(this.steamID, groupID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.follow = function(callback) {
|
||||
this._community.followUser(this.steamID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.unfollow = function(callback) {
|
||||
this._community.unfollowUser(this.steamID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.getAliases = function(callback) {
|
||||
this._community.getUserAliases(this.steamID, callback);
|
||||
};
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var SteamID = require('steamid');
|
||||
|
||||
SteamCommunity.ChatState = require('../resources/EChatState.js');
|
||||
SteamCommunity.PersonaState = require('../resources/EPersonaState.js');
|
||||
SteamCommunity.PersonaStateFlag = require('../resources/EPersonaStateFlag.js');
|
||||
|
||||
/**
|
||||
* @deprecated No support for new Steam chat. Use steam-user instead.
|
||||
* @param {int} interval
|
||||
* @param {string} uiMode
|
||||
*/
|
||||
SteamCommunity.prototype.chatLogon = function(interval, uiMode) {
|
||||
if(this.chatState == SteamCommunity.ChatState.LoggingOn || this.chatState == SteamCommunity.ChatState.LoggedOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
interval = interval || 500;
|
||||
uiMode = uiMode || "web";
|
||||
|
||||
this.emit('debug', 'Requesting chat WebAPI token');
|
||||
this.chatState = SteamCommunity.ChatState.LoggingOn;
|
||||
|
||||
var self = this;
|
||||
this.getWebApiOauthToken(function(err, token) {
|
||||
if(err) {
|
||||
var fatal = err.message.indexOf('not authorized') != -1;
|
||||
|
||||
if (!fatal) {
|
||||
self.chatState = SteamCommunity.ChatState.LogOnFailed;
|
||||
setTimeout(self.chatLogon.bind(self), 5000);
|
||||
} else {
|
||||
self.chatState = SteamCommunity.ChatState.Offline;
|
||||
}
|
||||
|
||||
self.emit('chatLogOnFailed', err, fatal);
|
||||
self.emit('debug', "Cannot get oauth token: " + err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
self.httpRequestPost({
|
||||
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logon/v1",
|
||||
"form": {
|
||||
"ui_mode": uiMode,
|
||||
"access_token": token
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(err || response.statusCode != 200) {
|
||||
self.chatState = SteamCommunity.ChatState.LogOnFailed;
|
||||
self.emit('chatLogOnFailed', err ? err : new Error("HTTP error " + response.statusCode), false);
|
||||
self.emit('debug', 'Error logging into webchat: ' + (err ? err.message : "HTTP error " + response.statusCode));
|
||||
setTimeout(self.chatLogon.bind(self), 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.error != 'OK') {
|
||||
self.chatState = SteamCommunity.ChatState.LogOnFailed;
|
||||
self.emit('chatLogOnFailed', new Error(body.error), false);
|
||||
self.emit('debug', 'Error logging into webchat: ' + body.error);
|
||||
setTimeout(self.chatLogon.bind(self), 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
self._chat = {
|
||||
"umqid": body.umqid,
|
||||
"message": body.message,
|
||||
"accessToken": token,
|
||||
"interval": interval,
|
||||
"uiMode": uiMode
|
||||
};
|
||||
|
||||
self.chatFriends = {};
|
||||
|
||||
self.chatState = SteamCommunity.ChatState.LoggedOn;
|
||||
self.emit('chatLoggedOn');
|
||||
self._chatPoll();
|
||||
}, "steamcommunity");
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated No support for new Steam chat. Use steam-user instead.
|
||||
* @param {string|SteamID} recipient
|
||||
* @param {string} text
|
||||
* @param {string} [type]
|
||||
* @param {function} [callback]
|
||||
*/
|
||||
SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) {
|
||||
if(this.chatState != SteamCommunity.ChatState.LoggedOn) {
|
||||
throw new Error("Chat must be logged on before messages can be sent");
|
||||
}
|
||||
|
||||
if(typeof recipient === 'string') {
|
||||
recipient = new SteamID(recipient);
|
||||
}
|
||||
|
||||
if(typeof type === 'function') {
|
||||
callback = type;
|
||||
type = 'saytext';
|
||||
}
|
||||
|
||||
type = type || 'saytext';
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Message/v1",
|
||||
"form": {
|
||||
"access_token": this._chat.accessToken,
|
||||
"steamid_dst": recipient.toString(),
|
||||
"text": text,
|
||||
"type": type,
|
||||
"umqid": this._chat.umqid
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.error != 'OK') {
|
||||
callback(new Error(body.error));
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated No support for new Steam chat. Use steam-user instead.
|
||||
*/
|
||||
SteamCommunity.prototype.chatLogoff = function() {
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logoff/v1",
|
||||
"form": {
|
||||
"access_token": this._chat.accessToken,
|
||||
"umqid": this._chat.umqid
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(err || response.statusCode != 200) {
|
||||
self.emit('debug', 'Error logging off of chat: ' + (err ? err.message : "HTTP error " + response.statusCode));
|
||||
setTimeout(self.chatLogoff.bind(self), 1000);
|
||||
} else {
|
||||
self.emit('chatLoggedOff');
|
||||
clearTimeout(self._chat.timer);
|
||||
delete self._chat;
|
||||
delete self.chatFriends;
|
||||
self.chatState = SteamCommunity.ChatState.Offline;
|
||||
}
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._chatPoll = function() {
|
||||
this.emit('debug', 'Doing chat poll');
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Poll/v1",
|
||||
"form": {
|
||||
"umqid": self._chat.umqid,
|
||||
"message": self._chat.message,
|
||||
"pollid": 1,
|
||||
"sectimeout": 20,
|
||||
"secidletime": 0,
|
||||
"use_accountids": 1,
|
||||
"access_token": self._chat.accessToken
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if (self.chatState == SteamCommunity.ChatState.Offline) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._chat.timer = setTimeout(self._chatPoll.bind(self), self._chat.interval);
|
||||
|
||||
if(err || response.statusCode != 200) {
|
||||
self.emit('debug', 'Error in chat poll: ' + (err ? err.message : "HTTP error " + response.statusCode));
|
||||
if (err.message == "Not Logged On") {
|
||||
self._relogWebChat();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body || body.error != 'OK') {
|
||||
self.emit('debug', 'Error in chat poll: ' + (body && body.error ? body.error : "Malformed response"));
|
||||
if (body && body.error && body.error == "Not Logged On") {
|
||||
self._relogWebChat();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self._chat.message = body.messagelast;
|
||||
|
||||
(body.messages || []).forEach(function(message) {
|
||||
var sender = new SteamID();
|
||||
sender.universe = SteamID.Universe.PUBLIC;
|
||||
sender.type = SteamID.Type.INDIVIDUAL;
|
||||
sender.instance = SteamID.Instance.DESKTOP;
|
||||
sender.accountid = message.accountid_from;
|
||||
|
||||
switch(message.type) {
|
||||
case 'personastate':
|
||||
self._chatUpdatePersona(sender);
|
||||
break;
|
||||
|
||||
case 'saytext':
|
||||
self.emit('chatMessage', sender, message.text);
|
||||
break;
|
||||
|
||||
case 'typing':
|
||||
self.emit('chatTyping', sender);
|
||||
break;
|
||||
|
||||
default:
|
||||
self.emit('debug', 'Unhandled chat message type: ' + message.type);
|
||||
}
|
||||
});
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._relogWebChat = function() {
|
||||
this.emit('debug', "Relogging web chat");
|
||||
clearTimeout(this._chat.timer);
|
||||
this.chatState = SteamCommunity.ChatState.Offline;
|
||||
this.chatLogon(this._chat.interval, this._chat.uiMode);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SteamID} steamID
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._chatUpdatePersona = function(steamID) {
|
||||
if (!this.chatFriends || this.chatState == SteamCommunity.ChatState.Offline) {
|
||||
return; // we no longer care
|
||||
}
|
||||
|
||||
this.emit('debug', 'Updating persona data for ' + steamID);
|
||||
var self = this;
|
||||
this.httpRequest({
|
||||
"uri": "https://steamcommunity.com/chat/friendstate/" + steamID.accountid,
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if (!self.chatFriends || self.chatState == SteamCommunity.ChatState.Offline) {
|
||||
return; // welp
|
||||
}
|
||||
|
||||
if(err || response.statusCode != 200) {
|
||||
self.emit('debug', 'Chat update persona error: ' + (err ? err.message : "HTTP error " + response.statusCode));
|
||||
setTimeout(function() {
|
||||
self._chatUpdatePersona(steamID);
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
var persona = {
|
||||
"steamID": steamID,
|
||||
"personaName": body.m_strName,
|
||||
"personaState": body.m_ePersonaState,
|
||||
"personaStateFlags": body.m_nPersonaStateFlags || 0,
|
||||
"avatarHash": body.m_strAvatarHash,
|
||||
"inGame": !!body.m_bInGame,
|
||||
"inGameAppID": body.m_nInGameAppID ? parseInt(body.m_nInGameAppID, 10) : null,
|
||||
"inGameName": body.m_strInGameName || null
|
||||
};
|
||||
|
||||
self.emit('chatPersonaState', steamID, persona);
|
||||
self.chatFriends[steamID.getSteamID64()] = persona;
|
||||
}, "steamcommunity");
|
||||
};
|
||||
@@ -1,47 +1,34 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var Cheerio = require('cheerio');
|
||||
var SteamTotp = require('steam-totp');
|
||||
var Async = require('async');
|
||||
const Cheerio = require('cheerio');
|
||||
const StdLib = require('@doctormckay/stdlib');
|
||||
const SteamTotp = require('steam-totp');
|
||||
|
||||
var CConfirmation = require('../classes/CConfirmation.js');
|
||||
var EConfirmationType = require('../resources/EConfirmationType.js');
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
const CConfirmation = require('../classes/CConfirmation.js');
|
||||
const EConfirmationType = SteamCommunity.EConfirmationType;
|
||||
|
||||
/**
|
||||
* Get a list of your account's currently outstanding confirmations.
|
||||
* @param {int} time - The unix timestamp with which the following key was generated
|
||||
* @param {string} key - The confirmation key that was generated using the preceeding time and the tag 'conf' (this key can be reused)
|
||||
* @param {SteamCommunity~getConfirmations} callback - Called when the list of confirmations is received
|
||||
* @param {SteamCommunity~getConfirmations} [callback] - Called when the list of confirmations is received
|
||||
* @return Promise<{confirmations: CConfirmation[]}>
|
||||
*/
|
||||
SteamCommunity.prototype.getConfirmations = function(time, key, callback) {
|
||||
var self = this;
|
||||
|
||||
// Ugly hack to maintain backward compatibility
|
||||
var tag = 'conf';
|
||||
if (typeof key == 'object') {
|
||||
tag = key.tag;
|
||||
key = key.key;
|
||||
}
|
||||
|
||||
// The official Steam app uses the tag 'list', but 'conf' still works so let's use that for backward compatibility.
|
||||
request(this, 'getlist', key, time, tag, null, true, function(err, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(['confirmations'], callback, false, async (resolve, reject) => {
|
||||
let body = await request(this, 'getlist', key, time, 'list', null);
|
||||
|
||||
if (!body.success) {
|
||||
if (body.needauth) {
|
||||
var err = new Error('Not Logged In');
|
||||
self._notifySessionExpired(err);
|
||||
callback(err);
|
||||
return;
|
||||
let err = new Error('Not Logged In');
|
||||
this._notifySessionExpired(err);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
callback(new Error(body.message || body.detail || 'Failed to get confirmation list'));
|
||||
return;
|
||||
return reject(new Error(body.message || body.detail || 'Failed to get confirmation list'));
|
||||
}
|
||||
|
||||
var confs = (body.conf || []).map(conf => new CConfirmation(self, {
|
||||
let confs = (body.conf || []).map(conf => new CConfirmation(this, {
|
||||
id: conf.id,
|
||||
type: conf.type,
|
||||
creator: conf.creator_id,
|
||||
@@ -54,14 +41,14 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) {
|
||||
icon: conf.icon || ''
|
||||
}));
|
||||
|
||||
callback(null, confs);
|
||||
resolve({confirmations: confs});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback SteamCommunity~getConfirmations
|
||||
* @param {Error|null} err - An Error object on failure, or null on success
|
||||
* @param {CConfirmation[]} confirmations - An array of CConfirmation objects
|
||||
* @param {CConfirmation[]} [confirmations] - An array of CConfirmation objects
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -69,29 +56,24 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) {
|
||||
* @param {int} confID - The ID of the confirmation in question
|
||||
* @param {int} time - The unix timestamp with which the following key was generated
|
||||
* @param {string} key - The confirmation key that was generated using the preceeding time and the tag "detail" (this key can be reused)
|
||||
* @param {SteamCommunity~getConfirmationOfferID} callback
|
||||
* @param {SteamCommunity~getConfirmationOfferID} [callback]
|
||||
* @return Promise<{offerID: string|null}>
|
||||
*/
|
||||
SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, callback) {
|
||||
// The official Steam app uses the tag 'detail', but 'details' still works so let's use that for backward compatibility
|
||||
request(this, 'detailspage/' + confID, key, time, 'details', null, false, function(err, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(['offerID'], callback, false, async (resolve, reject) => {
|
||||
let body = await request(this, 'detailspage/' + confID, key, time, 'detail', null);
|
||||
|
||||
if (typeof body != 'string') {
|
||||
callback(new Error("Cannot load confirmation details"));
|
||||
return;
|
||||
return reject(new Error('Cannot load confirmation details'));
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body);
|
||||
var offer = $('.tradeoffer');
|
||||
if(offer.length < 1) {
|
||||
callback(null, null);
|
||||
return;
|
||||
let $ = Cheerio.load(body);
|
||||
let offer = $('.tradeoffer');
|
||||
if (offer.length < 1) {
|
||||
return resolve({offerID: null});
|
||||
}
|
||||
|
||||
callback(null, offer.attr('id').split('_')[1]);
|
||||
resolve({offerID: offer.attr('id').split('_')[1]});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -103,47 +85,30 @@ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, ca
|
||||
|
||||
/**
|
||||
* Confirm or cancel a given confirmation.
|
||||
* @param {int|int[]} confID - The ID of the confirmation in question, or an array of confirmation IDs
|
||||
* @param {int|int[]|string|string[]} confID - The ID of the confirmation in question, or an array of confirmation IDs
|
||||
* @param {string|string[]} confKey - The confirmation key associated with the confirmation in question (or an array of them) (not a TOTP key, the `key` property of CConfirmation)
|
||||
* @param {int} time - The unix timestamp with which the following key was generated
|
||||
* @param {string} key - The confirmation key that was generated using the preceding time and the tag "allow" (if accepting) or "cancel" (if not accepting)
|
||||
* @param {boolean} accept - true if you want to accept the confirmation, false if you want to cancel it
|
||||
* @param {SteamCommunity~genericErrorCallback} callback - Called when the request is complete
|
||||
* @param {SteamCommunity~genericErrorCallback} [callback] - Called when the request is complete
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, key, accept, callback) {
|
||||
// Ugly hack to maintain backward compatibility
|
||||
var tag = accept ? 'allow' : 'cancel';
|
||||
if (typeof key == 'object') {
|
||||
tag = key.tag;
|
||||
key = key.key;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
let tag = accept ? 'accept' : 'reject';
|
||||
|
||||
// The official app uses tags reject/accept, but cancel/allow still works so use these for backward compatibility
|
||||
request(this, (confID instanceof Array) ? 'multiajaxop' : 'ajaxop', key, time, tag, {
|
||||
op: accept ? 'allow' : 'cancel',
|
||||
cid: confID,
|
||||
ck: confKey
|
||||
}, true, function(err, body) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
// The official app uses tags reject/accept, but cancel/allow still works so use these for backward compatibility
|
||||
let body = await request(this, (confID instanceof Array) ? 'multiajaxop' : 'ajaxop', key, time, tag, {
|
||||
op: accept ? 'allow' : 'cancel',
|
||||
cid: confID,
|
||||
ck: confKey
|
||||
});
|
||||
|
||||
if (body.success) {
|
||||
callback(null);
|
||||
return;
|
||||
return resolve();
|
||||
}
|
||||
|
||||
if (body.message) {
|
||||
callback(new Error(body.message));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(new Error('Could not act on confirmation'));
|
||||
reject(new Error(body.message || body.detail || 'Could not act on confirmation'));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -151,101 +116,79 @@ SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time,
|
||||
* Accept a confirmation for a given object (trade offer or market listing) automatically.
|
||||
* @param {string} identitySecret
|
||||
* @param {number|string} objectID
|
||||
* @param {SteamCommunity~genericErrorCallback} callback
|
||||
* @param {SteamCommunity~genericErrorCallback} [callback]
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, objectID, callback) {
|
||||
var self = this;
|
||||
this._usedConfTimes = this._usedConfTimes || [];
|
||||
|
||||
if (typeof this._timeOffset !== 'undefined') {
|
||||
// time offset is already known and saved
|
||||
doConfirmation();
|
||||
} else {
|
||||
SteamTotp.getTimeOffset(function(err, offset) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
// Figure out our time offset
|
||||
if (typeof this._timeOffset == 'undefined') {
|
||||
await new Promise((resolve) => {
|
||||
SteamTotp.getTimeOffset((err, offset) => {
|
||||
if (err) {
|
||||
// not critical that this succeeds
|
||||
return resolve();
|
||||
}
|
||||
|
||||
self._timeOffset = offset;
|
||||
doConfirmation();
|
||||
this._timeOffset = offset;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
// Delete the saved time offset after 12 hours because why not
|
||||
delete self._timeOffset;
|
||||
}, 1000 * 60 * 60 * 12).unref();
|
||||
});
|
||||
}
|
||||
let offset = this._timeOffset;
|
||||
let time = SteamTotp.time(offset);
|
||||
let key = SteamTotp.getConfirmationKey(identitySecret, time, 'list');
|
||||
let {confirmations} = await this.getConfirmations(time, key);
|
||||
|
||||
function doConfirmation() {
|
||||
var offset = self._timeOffset;
|
||||
var time = SteamTotp.time(offset);
|
||||
var confKey = SteamTotp.getConfirmationKey(identitySecret, time, 'list');
|
||||
self.getConfirmations(time, {tag: 'list', key: confKey}, function(err, confs) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
let conf = confirmations.find(conf => conf.creator == objectID);
|
||||
if (!conf) {
|
||||
return reject(new Error(`Could not find confirmation for object ${objectID}`));
|
||||
}
|
||||
|
||||
var conf = confs.filter(function(conf) { return conf.creator == objectID; });
|
||||
if (conf.length == 0) {
|
||||
callback(new Error('Could not find confirmation for object ' + objectID));
|
||||
return;
|
||||
}
|
||||
// make sure we don't reuse the same time
|
||||
let localOffset = 0;
|
||||
do {
|
||||
time = SteamTotp.time(offset) + localOffset++;
|
||||
} while (this._usedConfTimes.includes(time));
|
||||
|
||||
conf = conf[0];
|
||||
this._usedConfTimes.push(time);
|
||||
if (this._usedConfTimes.length > 60) {
|
||||
this._usedConfTimes.splice(0, this._usedConfTimes.length - 60); // we don't need to save more than 60 entries
|
||||
}
|
||||
|
||||
// make sure we don't reuse the same time
|
||||
var localOffset = 0;
|
||||
do {
|
||||
time = SteamTotp.time(offset) + localOffset++;
|
||||
} while (self._usedConfTimes.indexOf(time) != -1);
|
||||
|
||||
self._usedConfTimes.push(time);
|
||||
if (self._usedConfTimes.length > 60) {
|
||||
self._usedConfTimes.splice(0, self._usedConfTimes.length - 60); // we don't need to save more than 60 entries
|
||||
}
|
||||
|
||||
confKey = SteamTotp.getConfirmationKey(identitySecret, time, 'accept');
|
||||
conf.respond(time, {tag: 'accept', key: confKey}, true, callback);
|
||||
});
|
||||
}
|
||||
await conf.respond(time, SteamTotp.getConfirmationKey(identitySecret, time, 'accept'), true);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a single request to Steam to accept all outstanding confirmations (after loading the list). If one fails, the
|
||||
* entire request will fail and there will be no way to know which failed without loading the list again.
|
||||
* @param {number} time
|
||||
* @param {string} confKey
|
||||
* @param {string} allowKey
|
||||
* @param {function} callback
|
||||
* @param {string} listKey
|
||||
* @param {string} acceptKey
|
||||
* @param {function} [callback]
|
||||
* @return Promise<{confirmations: CConfirmation[]}>
|
||||
*/
|
||||
SteamCommunity.prototype.acceptAllConfirmations = function(time, confKey, allowKey, callback) {
|
||||
var self = this;
|
||||
SteamCommunity.prototype.acceptAllConfirmations = function(time, listKey, acceptKey, callback) {
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
let {confirmations} = await this.getConfirmations(time, listKey);
|
||||
|
||||
this.getConfirmations(time, confKey, function(err, confs) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
if (confirmations.length == 0) {
|
||||
return resolve({confirmations: []});
|
||||
}
|
||||
|
||||
if (confs.length == 0) {
|
||||
callback(null, []);
|
||||
return;
|
||||
}
|
||||
let confIds = confirmations.map(conf => conf.id);
|
||||
let confKeys = confirmations.map(conf => conf.key);
|
||||
await this.respondToConfirmation(confIds, confKeys, time, acceptKey, true);
|
||||
|
||||
self.respondToConfirmation(confs.map(function(conf) { return conf.id; }), confs.map(function(conf) { return conf.key; }), time, allowKey, true, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(err, confs);
|
||||
});
|
||||
resolve({confirmations});
|
||||
});
|
||||
};
|
||||
|
||||
function request(community, url, key, time, tag, params, json, callback) {
|
||||
async function request(community, url, key, time, tag, params) {
|
||||
if (!community.steamID) {
|
||||
throw new Error('Must be logged in before trying to do anything with confirmations');
|
||||
}
|
||||
@@ -258,10 +201,10 @@ function request(community, url, key, time, tag, params, json, callback) {
|
||||
params.m = 'react';
|
||||
params.tag = tag;
|
||||
|
||||
var req = {
|
||||
let req = {
|
||||
method: url == 'multiajaxop' ? 'POST' : 'GET',
|
||||
uri: 'https://steamcommunity.com/mobileconf/' + url,
|
||||
json: !!json
|
||||
url: `https://steamcommunity.com/mobileconf/${url}`,
|
||||
source: 'steamcommunity'
|
||||
};
|
||||
|
||||
if (req.method == 'GET') {
|
||||
@@ -270,159 +213,6 @@ function request(community, url, key, time, tag, params, json, callback) {
|
||||
req.form = params;
|
||||
}
|
||||
|
||||
community.httpRequest(req, function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, body);
|
||||
}, 'steamcommunity');
|
||||
let result = await community.httpRequest(req);
|
||||
return result.jsonBody || result.textBody;
|
||||
}
|
||||
|
||||
// Confirmation checker
|
||||
|
||||
/**
|
||||
* Start automatically polling our confirmations for new ones. The `confKeyNeeded` event will be emitted when we need a confirmation key, or `newConfirmation` when we get a new confirmation
|
||||
* @param {int} pollInterval - The interval, in milliseconds, at which we will poll for confirmations. This should probably be at least 10,000 to avoid rate-limits.
|
||||
* @param {Buffer|string|null} [identitySecret=null] - Your identity_secret. If passed, all confirmations will be automatically accepted and nothing will be emitted.
|
||||
*/
|
||||
SteamCommunity.prototype.startConfirmationChecker = function(pollInterval, identitySecret) {
|
||||
this._confirmationPollInterval = pollInterval;
|
||||
this._knownConfirmations = this._knownConfirmations || {};
|
||||
this._confirmationKeys = this._confirmationKeys || {};
|
||||
this._identitySecret = identitySecret;
|
||||
|
||||
if(this._confirmationTimer) {
|
||||
clearTimeout(this._confirmationTimer);
|
||||
}
|
||||
|
||||
setTimeout(this.checkConfirmations.bind(this), 500);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop automatically polling our confirmations.
|
||||
*/
|
||||
SteamCommunity.prototype.stopConfirmationChecker = function() {
|
||||
if(this._confirmationPollInterval) {
|
||||
delete this._confirmationPollInterval;
|
||||
}
|
||||
|
||||
if(this._identitySecret) {
|
||||
delete this._identitySecret;
|
||||
}
|
||||
|
||||
if(this._confirmationTimer) {
|
||||
clearTimeout(this._confirmationTimer);
|
||||
delete this._confirmationTimer;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Run the confirmation checker right now instead of waiting for the next poll.
|
||||
* Useful to call right after you send/accept an offer that needs confirmation.
|
||||
*/
|
||||
SteamCommunity.prototype.checkConfirmations = function() {
|
||||
if(this._confirmationTimer) {
|
||||
clearTimeout(this._confirmationTimer);
|
||||
delete this._confirmationTimer;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
if(!this._confirmationQueue) {
|
||||
this._confirmationQueue = Async.queue(function(conf, callback) {
|
||||
// Worker to process new confirmations
|
||||
if(self._identitySecret) {
|
||||
// We should accept this
|
||||
self.emit('debug', "Accepting confirmation #" + conf.id);
|
||||
var time = Math.floor(Date.now() / 1000);
|
||||
conf.respond(time, SteamTotp.getConfirmationKey(self._identitySecret, time, "allow"), true, function(err) {
|
||||
// If there was an error and it wasn't actually accepted, we'll pick it up again
|
||||
if (!err) self.emit('confirmationAccepted', conf);
|
||||
delete self._knownConfirmations[conf.id];
|
||||
setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes
|
||||
});
|
||||
} else {
|
||||
self.emit('newConfirmation', conf);
|
||||
setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
this.emit('debug', 'Checking confirmations');
|
||||
|
||||
this._confirmationCheckerGetKey('conf', function(err, key) {
|
||||
if(err) {
|
||||
resetTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
self.getConfirmations(key.time, key.key, function(err, confirmations) {
|
||||
if(err) {
|
||||
self.emit('debug', "Can't check confirmations: " + err.message);
|
||||
resetTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
var known = self._knownConfirmations;
|
||||
|
||||
var newOnes = confirmations.filter(function(conf) {
|
||||
return !known[conf.id];
|
||||
});
|
||||
|
||||
if(newOnes.length < 1) {
|
||||
resetTimer();
|
||||
return; // No new ones
|
||||
}
|
||||
|
||||
// We have new confirmations!
|
||||
newOnes.forEach(function(conf) {
|
||||
self._knownConfirmations[conf.id] = conf; // Add it to our list of known confirmations
|
||||
self._confirmationQueue.push(conf);
|
||||
});
|
||||
|
||||
resetTimer();
|
||||
});
|
||||
});
|
||||
|
||||
function resetTimer() {
|
||||
if(self._confirmationPollInterval) {
|
||||
self._confirmationTimer = setTimeout(self.checkConfirmations.bind(self), self._confirmationPollInterval);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._confirmationCheckerGetKey = function(tag, callback) {
|
||||
if(this._identitySecret) {
|
||||
if(tag == 'details') {
|
||||
// We don't care about details
|
||||
callback(new Error("Disabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
var time = Math.floor(Date.now() / 1000);
|
||||
callback(null, {"time": time, "key": SteamTotp.getConfirmationKey(this._identitySecret, time, tag)});
|
||||
return;
|
||||
}
|
||||
|
||||
var existing = this._confirmationKeys[tag];
|
||||
var reusable = ['conf', 'details'];
|
||||
|
||||
// See if we already have a key that we can reuse.
|
||||
if(reusable.indexOf(tag) != -1 && existing && (Date.now() - (existing.time * 1000) < (1000 * 60 * 5))) {
|
||||
callback(null, existing);
|
||||
return;
|
||||
}
|
||||
|
||||
// We need a fresh one
|
||||
var self = this;
|
||||
this.emit('confKeyNeeded', tag, function(err, time, key) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
self._confirmationKeys[tag] = {"time": time, "key": key};
|
||||
callback(null, {"time": time, "key": key});
|
||||
});
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,24 +1,32 @@
|
||||
const SteamCommunity = require('../index.js');
|
||||
const StdLib = require('@doctormckay/stdlib');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {HttpResponse} = require('@doctormckay/stdlib/http');
|
||||
|
||||
const Helpers = require('./helpers.js');
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
const HELP_SITE_DOMAIN = 'https://help.steampowered.com';
|
||||
|
||||
/**
|
||||
* Restore a previously removed steam package from your steam account.
|
||||
* @param {int|string} packageID
|
||||
* @param {function} callback
|
||||
* @param {function} [callback]
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.restorePackage = function(packageID, callback) {
|
||||
this.httpRequestPost({
|
||||
uri: HELP_SITE_DOMAIN + '/wizard/AjaxDoPackageRestore',
|
||||
form: {
|
||||
packageid: packageID,
|
||||
sessionid: this.getSessionID(HELP_SITE_DOMAIN),
|
||||
wizard_ajax: 1
|
||||
},
|
||||
json: true
|
||||
}, wizardAjaxHandler(callback));
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
let result = await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: `${HELP_SITE_DOMAIN}/wizard/AjaxDoPackageRestore`,
|
||||
form: {
|
||||
packageid: packageID,
|
||||
sessionid: this.getSessionID(HELP_SITE_DOMAIN),
|
||||
wizard_ajax: 1
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
wizardAjaxHandler(result, resolve, reject);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -27,38 +35,32 @@ SteamCommunity.prototype.restorePackage = function(packageID, callback) {
|
||||
* @param {function} callback
|
||||
*/
|
||||
SteamCommunity.prototype.removePackage = function(packageID, callback) {
|
||||
this.httpRequestPost({
|
||||
uri: HELP_SITE_DOMAIN + '/wizard/AjaxDoPackageRemove',
|
||||
form: {
|
||||
packageid: packageID,
|
||||
sessionid: this.getSessionID(HELP_SITE_DOMAIN),
|
||||
wizard_ajax: 1
|
||||
},
|
||||
json: true
|
||||
}, wizardAjaxHandler(callback));
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
let result = await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: `${HELP_SITE_DOMAIN}/wizard/AjaxDoPackageRemove`,
|
||||
form: {
|
||||
packageid: packageID,
|
||||
sessionid: this.getSessionID(HELP_SITE_DOMAIN),
|
||||
wizard_ajax: 1
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
wizardAjaxHandler(result, resolve, reject);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a handler for wizard ajax HTTP requests.
|
||||
* @param {function} callback
|
||||
* @returns {(function(*=, *, *): void)|*}
|
||||
*
|
||||
* @param {HttpResponse} result
|
||||
* @param {function} resolve
|
||||
* @param {function} reject
|
||||
*/
|
||||
function wizardAjaxHandler(callback) {
|
||||
return (err, res, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
function wizardAjaxHandler(result, resolve, reject) {
|
||||
if (!result.jsonBody || !result.jsonBody.success) {
|
||||
return reject(new Error((result.jsonBody || {}).errorMsg || 'Unexpected error'));
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!body.success) {
|
||||
callback(body.errorMsg ? new Error(body.errorMsg) : Helpers.eresultError(body.success));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
};
|
||||
resolve();
|
||||
}
|
||||
|
||||
@@ -1,41 +1,32 @@
|
||||
const request = require('request');
|
||||
const SteamID = require('steamid');
|
||||
const xml2js = require('xml2js');
|
||||
|
||||
const EResult = require('../resources/EResult.js');
|
||||
|
||||
/**
|
||||
* Make sure that a provided input is a valid SteamID object.
|
||||
* @param {object} input
|
||||
* @returns {boolean}
|
||||
*/
|
||||
exports.isSteamID = function(input) {
|
||||
var keys = Object.keys(input);
|
||||
if (keys.length != 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure it has the keys we expect
|
||||
keys = keys.filter(function(item) {
|
||||
return ['universe', 'type', 'instance', 'accountid'].indexOf(item) != -1;
|
||||
});
|
||||
|
||||
return keys.length == 4;
|
||||
return ['universe', 'type', 'instance', 'accountid'].every(prop => typeof input[prop] == 'number' || typeof input[prop] == 'bigint');
|
||||
};
|
||||
|
||||
exports.decodeSteamTime = function(time) {
|
||||
var date = new Date();
|
||||
let date = new Date();
|
||||
|
||||
if (time.includes("@")) {
|
||||
var parts = time.split('@');
|
||||
if (!parts[0].includes(",")) {
|
||||
if (time.includes('@')) {
|
||||
let parts = time.split('@');
|
||||
if (!parts[0].includes(',')) {
|
||||
// no year, assume current year
|
||||
parts[0] += ", " + date.getFullYear();
|
||||
parts[0] += ', ' + date.getFullYear();
|
||||
}
|
||||
|
||||
date = new Date(parts.join('@').replace(/(am|pm)/, ' $1') + " UTC"); // add a space so JS can decode it
|
||||
date = new Date(parts.join('@').replace(/(am|pm)/, ' $1') + ' UTC'); // add a space so JS can decode it
|
||||
} else {
|
||||
// Relative date
|
||||
var amount = time.replace(/(\d) (minutes|hour|hours) ago/, "$1");
|
||||
let amount = time.replace(/(\d) (minutes|hour|hours) ago/, '$1');
|
||||
|
||||
if(time.includes("minutes")) {
|
||||
if (time.includes('minutes')) {
|
||||
date.setMinutes(date.getMinutes() - amount);
|
||||
} else if(time.match(/hour|hours/)) {
|
||||
} else if (time.match(/hour|hours/)) {
|
||||
date.setHours(date.getHours() - amount);
|
||||
}
|
||||
}
|
||||
@@ -45,16 +36,17 @@ exports.decodeSteamTime = function(time) {
|
||||
|
||||
/**
|
||||
* Get an Error object for a particular EResult
|
||||
* @param {int} eresult
|
||||
* @param {int|EResult} eresult
|
||||
* @param {string} [message] - If eresult is a failure code and message exists, this message will be used in the Error object instead
|
||||
* @returns {null|Error}
|
||||
*/
|
||||
exports.eresultError = function(eresult) {
|
||||
exports.eresultError = function(eresult, message) {
|
||||
if (eresult == EResult.OK) {
|
||||
// no error
|
||||
return null;
|
||||
}
|
||||
|
||||
var err = new Error(EResult[eresult] || ("Error " + eresult));
|
||||
let err = new Error(message || EResult[eresult] || `Error ${eresult}`);
|
||||
err.eresult = eresult;
|
||||
return err;
|
||||
};
|
||||
@@ -70,59 +62,3 @@ exports.decodeJwt = function(jwt) {
|
||||
|
||||
return JSON.parse(Buffer.from(standardBase64, 'base64').toString('utf8'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves a Steam profile URL to get steamID64 and vanityURL
|
||||
* @param {String} url - Full steamcommunity profile URL or only the vanity part.
|
||||
* @param {Object} callback - First argument is null/Error, second is object containing vanityURL (String) and steamID (String)
|
||||
*/
|
||||
exports.resolveVanityURL = function(url, callback) {
|
||||
// Precede url param if only the vanity was provided
|
||||
if (!url.includes("steamcommunity.com")) {
|
||||
url = "https://steamcommunity.com/id/" + url;
|
||||
}
|
||||
|
||||
// Make request to get XML data
|
||||
request(url + "/?xml=1", function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse XML data returned from Steam into an object
|
||||
new xml2js.Parser().parseString(body, (err, parsed) => {
|
||||
if (err) {
|
||||
callback(new Error("Couldn't parse XML response"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsed.response && parsed.response.error) {
|
||||
callback(new Error("Couldn't find Steam ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
let steamID64 = parsed.profile.steamID64[0];
|
||||
let vanityURL = parsed.profile.customURL[0];
|
||||
|
||||
callback(null, {"vanityURL": vanityURL, "steamID": steamID64});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts `input` into a SteamID object, if it's a parseable string.
|
||||
* @param {SteamID|string} input
|
||||
* @return {SteamID}
|
||||
*/
|
||||
exports.steamID = function(input) {
|
||||
if (exports.isSteamID(input)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (typeof input != 'string') {
|
||||
throw new Error(`Input SteamID value "${input}" is not a string`);
|
||||
}
|
||||
|
||||
// This will throw if the input is not a well-formed SteamID
|
||||
return new SteamID(input);
|
||||
};
|
||||
|
||||
@@ -1,144 +1,166 @@
|
||||
var URL = require('url');
|
||||
const {HttpResponse} = require('@doctormckay/stdlib/http'); // eslint-disable-line
|
||||
const {betterPromise} = require('@doctormckay/stdlib/promises');
|
||||
|
||||
var SteamCommunity = require('../index.js');
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) {
|
||||
if (typeof uri === 'object') {
|
||||
source = callback;
|
||||
callback = options;
|
||||
options = uri;
|
||||
uri = options.url || options.uri;
|
||||
} else if (typeof options === 'function') {
|
||||
source = callback;
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {string} options.method
|
||||
* @param {string} options.url
|
||||
* @param {string} [options.source='']
|
||||
* @param {object} [options.qs]
|
||||
* @param {*} [options.body]
|
||||
* @param {object} [options.form]
|
||||
* @param {object} [options.multipartForm]
|
||||
* @param {boolean} [options.json=false] - Controls whether the *REQUEST* should be sent as json.
|
||||
* @param {boolean} [options.followRedirect=true]
|
||||
* @param {boolean} [options.checkHttpError=true]
|
||||
* @param {boolean} [options.checkCommunityError=true]
|
||||
* @param {boolean} [options.checkTradeError=true]
|
||||
* @param {boolean} [options.checkJsonError=true]
|
||||
* @return {Promise<HttpResponse>}
|
||||
*/
|
||||
SteamCommunity.prototype.httpRequest = function(options) {
|
||||
return betterPromise(async (resolve, reject) => {
|
||||
let requestID = ++this._httpRequestID;
|
||||
let source = options.source || '';
|
||||
|
||||
options.url = options.uri = uri;
|
||||
|
||||
if (this._httpRequestConvenienceMethod) {
|
||||
options.method = this._httpRequestConvenienceMethod;
|
||||
delete this._httpRequestConvenienceMethod;
|
||||
}
|
||||
|
||||
// Add origin header if necessary
|
||||
// https://github.com/DoctorMcKay/node-steamcommunity/issues/351
|
||||
if ((options.method || 'GET').toUpperCase() != 'GET') {
|
||||
options.headers = options.headers || {};
|
||||
if (!options.headers.origin) {
|
||||
var parsedUrl = URL.parse(options.url);
|
||||
options.headers.origin = parsedUrl.protocol + '//' + parsedUrl.host;
|
||||
}
|
||||
}
|
||||
|
||||
var requestID = ++this._httpRequestID;
|
||||
source = source || "";
|
||||
|
||||
var self = this;
|
||||
var continued = false;
|
||||
|
||||
if (!this.onPreHttpRequest || !this.onPreHttpRequest(requestID, source, options, continueRequest)) {
|
||||
// No pre-hook, or the pre-hook doesn't want to delay the request.
|
||||
continueRequest(null);
|
||||
}
|
||||
|
||||
function continueRequest(err) {
|
||||
if (continued) {
|
||||
return;
|
||||
}
|
||||
|
||||
continued = true;
|
||||
|
||||
if (err) {
|
||||
if (callback) {
|
||||
callback(err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self.request(options, function (err, response, body) {
|
||||
var hasCallback = !!callback;
|
||||
var httpError = options.checkHttpError !== false && self._checkHttpError(err, response, callback, body);
|
||||
var communityError = !options.json && options.checkCommunityError !== false && self._checkCommunityError(body, httpError ? function () {} : callback); // don't fire the callback if hasHttpError did it already
|
||||
var tradeError = !options.json && options.checkTradeError !== false && self._checkTradeError(body, httpError || communityError ? function () {} : callback); // don't fire the callback if either of the previous already did
|
||||
var jsonError = options.json && options.checkJsonError !== false && !body ? new Error("Malformed JSON response") : null;
|
||||
|
||||
self.emit('postHttpRequest', requestID, source, options, httpError || communityError || tradeError || jsonError || null, response, body, {
|
||||
"hasCallback": hasCallback,
|
||||
"httpError": httpError,
|
||||
"communityError": communityError,
|
||||
"tradeError": tradeError,
|
||||
"jsonError": jsonError
|
||||
});
|
||||
|
||||
if (hasCallback && !(httpError || communityError || tradeError)) {
|
||||
if (jsonError) {
|
||||
callback.call(self, jsonError, response);
|
||||
} else {
|
||||
callback.apply(self, arguments);
|
||||
}
|
||||
await betterPromise((resolve, reject) => {
|
||||
if (!this.onPreHttpRequest || !this.onPreHttpRequest(requestID, source, options, (err) => {
|
||||
err ? reject(err) : resolve();
|
||||
})) {
|
||||
// No pre-hook, or the pre-hook doesn't want to delay the request.
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
let result = await this._httpClient.request({
|
||||
method: options.method,
|
||||
url: options.url,
|
||||
queryString: options.qs,
|
||||
headers: options.headers,
|
||||
body: options.body,
|
||||
urlEncodedForm: options.form,
|
||||
multipartForm: options.multipartForm,
|
||||
json: options.json,
|
||||
followRedirects: options.followRedirect
|
||||
});
|
||||
|
||||
let httpError = options.checkHttpError !== false && this._checkHttpError(result);
|
||||
let communityError = !options.json && options.checkCommunityError !== false && this._checkCommunityError(result);
|
||||
let tradeError = !options.json && options.checkTradeError !== false && this._checkTradeError(result);
|
||||
let jsonError = options.json && options.checkJsonError !== false && !result.jsonBody ? new Error('Malformed JSON response') : null;
|
||||
|
||||
this.emit('postHttpRequest', {
|
||||
requestID,
|
||||
source,
|
||||
options,
|
||||
response: result,
|
||||
body: result.textBody,
|
||||
error: httpError || communityError || tradeError || jsonError || null,
|
||||
httpError,
|
||||
communityError,
|
||||
tradeError,
|
||||
jsonError
|
||||
});
|
||||
|
||||
resolve(result);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string|object} endpoint
|
||||
* @param {object} [form]
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._myProfile = async function(endpoint, form) {
|
||||
if (!this._profileURL) {
|
||||
let result = await this.httpRequest({
|
||||
method: 'GET',
|
||||
url: 'https://steamcommunity.com/my',
|
||||
followRedirect: false,
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
if (result.statusCode != 302) {
|
||||
throw new Error(`HTTP error ${result.statusCode}`);
|
||||
}
|
||||
|
||||
let match = result.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^/]+)\/?/);
|
||||
if (!match) {
|
||||
throw new Error('Can\'t get profile URL');
|
||||
}
|
||||
|
||||
this._profileURL = match[1];
|
||||
setTimeout(() => {
|
||||
delete this._profileURL; // delete the cache
|
||||
}, 60000).unref();
|
||||
}
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.httpRequestGet = function() {
|
||||
this._httpRequestConvenienceMethod = "GET";
|
||||
return this.httpRequest.apply(this, arguments);
|
||||
};
|
||||
let options = endpoint.endpoint ? endpoint : {};
|
||||
options.url = `https://steamcommunity.com${this._profileURL}/${endpoint.endpoint || endpoint}`;
|
||||
options.followRedirect = true;
|
||||
|
||||
SteamCommunity.prototype.httpRequestPost = function() {
|
||||
this._httpRequestConvenienceMethod = "POST";
|
||||
return this.httpRequest.apply(this, arguments);
|
||||
if (form) {
|
||||
options.method = 'POST';
|
||||
options.form = form;
|
||||
} else if (!options.method) {
|
||||
options.method = 'GET';
|
||||
}
|
||||
|
||||
options.source = 'steamcommunity';
|
||||
|
||||
return await this.httpRequest(options);
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._notifySessionExpired = function(err) {
|
||||
this.emit('sessionExpired', err);
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._checkHttpError = function(err, response, callback, body) {
|
||||
if (err) {
|
||||
callback(err, response, body);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HttpResponse} response
|
||||
* @return {Error|boolean}
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._checkHttpError = function(response) {
|
||||
if (response.statusCode >= 300 && response.statusCode <= 399 && response.headers.location.indexOf('/login') != -1) {
|
||||
err = new Error("Not Logged In");
|
||||
callback(err, response, body);
|
||||
let err = new Error('Not Logged In');
|
||||
this._notifySessionExpired(err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (response.statusCode == 403 && typeof response.body === 'string' && response.body.match(/<div id="parental_notice_instructions">Enter your PIN below to exit Family View.<\/div>/)) {
|
||||
err = new Error("Family View Restricted");
|
||||
callback(err, response, body);
|
||||
return err;
|
||||
if (
|
||||
response.statusCode == 403
|
||||
&& typeof response.textBody == 'string'
|
||||
&& response.textBody.match(/<div id="parental_notice_instructions">Enter your PIN below to exit Family View.<\/div>/)
|
||||
) {
|
||||
return new Error('Family View Restricted');
|
||||
}
|
||||
|
||||
if (response.statusCode >= 400) {
|
||||
err = new Error("HTTP error " + response.statusCode);
|
||||
let err = new Error(`HTTP error ${response.statusCode}`);
|
||||
err.code = response.statusCode;
|
||||
callback(err, response, body);
|
||||
return err;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._checkCommunityError = function(html, callback) {
|
||||
var err;
|
||||
/**
|
||||
* @param {HttpResponse} response
|
||||
* @return {Error|boolean}
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._checkCommunityError = function(response) {
|
||||
let html = response.textBody;
|
||||
|
||||
if(typeof html === 'string' && html.match(/<h1>Sorry!<\/h1>/)) {
|
||||
var match = html.match(/<h3>(.+)<\/h3>/);
|
||||
err = new Error(match ? match[1] : "Unknown error occurred");
|
||||
callback(err);
|
||||
return err;
|
||||
if (typeof html == 'string' && html.match(/<h1>Sorry!<\/h1>/)) {
|
||||
let match = html.match(/<h3>(.+)<\/h3>/);
|
||||
return new Error(match ? match[1] : 'Unknown error occurred');
|
||||
}
|
||||
|
||||
if (typeof html === 'string' && html.indexOf('g_steamID = false;') > -1 && html.indexOf('<title>Sign In</title>') > -1) {
|
||||
err = new Error("Not Logged In");
|
||||
callback(err);
|
||||
if (typeof html == 'string' && html.indexOf('g_steamID = false;') > -1 && html.indexOf('<title>Sign In</title>') > -1) {
|
||||
let err = new Error('Not Logged In');
|
||||
this._notifySessionExpired(err);
|
||||
return err;
|
||||
}
|
||||
@@ -146,16 +168,21 @@ SteamCommunity.prototype._checkCommunityError = function(html, callback) {
|
||||
return false;
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._checkTradeError = function(html, callback) {
|
||||
/**
|
||||
* @param {HttpResponse} response
|
||||
* @return {Error|boolean}
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._checkTradeError = function(response) {
|
||||
let html = response.textBody;
|
||||
|
||||
if (typeof html !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var match = html.match(/<div id="error_msg">\s*([^<]+)\s*<\/div>/);
|
||||
let match = html.match(/<div id="error_msg">\s*([^<]+)\s*<\/div>/);
|
||||
if (match) {
|
||||
var err = new Error(match[1].trim());
|
||||
callback(err);
|
||||
return err;
|
||||
return new Error(match[1].trim());
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var CEconItem = require('../classes/CEconItem.js');
|
||||
var Helpers = require('./helpers.js');
|
||||
var SteamID = require('steamid');
|
||||
var request = require('request');
|
||||
var Cheerio = require('cheerio');
|
||||
var Async = require('async');
|
||||
|
||||
/*
|
||||
* Inventory history in a nutshell.
|
||||
*
|
||||
* There are no more page numbers. Now you have to request after_time and optionally after_trade.
|
||||
* Without "prev" set, you will request 30 trades that were completed FURTHER IN THE PAST than after_time (and optionally after_trade)
|
||||
* With "prev" set, you will request 30 trades that were completed MORE RECENTLY than after_time (and optionally after_trade)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @deprecated Use GetTradeHistory instead: https://lab.xpaw.me/steam_api_documentation.html#IEconService_GetTradeHistory_v1
|
||||
* @param {object} options
|
||||
* @param {function} callback
|
||||
*/
|
||||
SteamCommunity.prototype.getInventoryHistory = function(options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.direction = options.direction || "past";
|
||||
|
||||
var qs = "?l=english";
|
||||
if (options.startTime) {
|
||||
if (options.startTime instanceof Date) {
|
||||
options.startTime = Math.floor(options.startTime.getTime() / 1000);
|
||||
}
|
||||
|
||||
qs += "&after_time=" + options.startTime;
|
||||
|
||||
if (options.startTrade) {
|
||||
qs += "&after_trade=" + options.startTrade;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.direction == "future") {
|
||||
qs += "&prev=1";
|
||||
}
|
||||
|
||||
this._myProfile("inventoryhistory" + qs, null, function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var output = {};
|
||||
var vanityURLs = [];
|
||||
|
||||
var $ = Cheerio.load(body);
|
||||
if (!$('.inventory_history_pagingrow').html()) {
|
||||
callback(new Error("Malformed page: no paging row found"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the inventory item data
|
||||
var match2 = body.match(/var g_rgHistoryInventory = (.*);/);
|
||||
if (!match2) {
|
||||
callback(new Error("Malformed page: no trade found"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var historyInventory = JSON.parse(match2[1]);
|
||||
} catch (ex) {
|
||||
callback(new Error("Malformed page: no well-formed trade data found"));
|
||||
return;
|
||||
}
|
||||
|
||||
var i;
|
||||
|
||||
// See if we've got paging buttons
|
||||
var $paging = $('.inventory_history_nextbtn .pagebtn:not(.disabled)');
|
||||
var href;
|
||||
for (i = 0; i < $paging.length; i++) {
|
||||
href = $paging[i].attribs.href;
|
||||
if (href.match(/prev=1/)) {
|
||||
output.firstTradeTime = new Date(href.match(/after_time=(\d+)/)[1] * 1000);
|
||||
output.firstTradeID = href.match(/after_trade=(\d+)/)[1];
|
||||
} else {
|
||||
output.lastTradeTime = new Date(href.match(/after_time=(\d+)/)[1] * 1000);
|
||||
output.lastTradeID = href.match(/after_trade=(\d+)/)[1];
|
||||
}
|
||||
}
|
||||
|
||||
output.trades = [];
|
||||
var trades = $('.tradehistoryrow');
|
||||
|
||||
var item, trade, profileLink, items, j, econItem, timeMatch, time;
|
||||
for (i = 0; i < trades.length; i++) {
|
||||
item = $(trades[i]);
|
||||
trade = {};
|
||||
|
||||
trade.onHold = !!item.find('span:nth-of-type(2)').text().match(/Trade on Hold/i);
|
||||
|
||||
timeMatch = item.find('.tradehistory_timestamp').html().match(/(\d+):(\d+)(am|pm)/);
|
||||
if (timeMatch[1] == 12 && timeMatch[3] == 'am') {
|
||||
timeMatch[1] = 0;
|
||||
}
|
||||
|
||||
if (timeMatch[1] < 12 && timeMatch[3] == 'pm') {
|
||||
timeMatch[1] = parseInt(timeMatch[1], 10) + 12;
|
||||
}
|
||||
|
||||
time = (timeMatch[1] < 10 ? '0' : '') + timeMatch[1] + ':' + timeMatch[2] + ':00';
|
||||
|
||||
trade.date = new Date(item.find('.tradehistory_date').html() + ' ' + time + ' UTC');
|
||||
trade.partnerName = item.find('.tradehistory_event_description a').html();
|
||||
trade.partnerSteamID = null;
|
||||
trade.partnerVanityURL = null;
|
||||
trade.itemsReceived = [];
|
||||
trade.itemsGiven = [];
|
||||
|
||||
profileLink = item.find('.tradehistory_event_description a').attr('href');
|
||||
if (profileLink.indexOf('/profiles/') != -1) {
|
||||
trade.partnerSteamID = new SteamID(profileLink.match(/(\d+)$/)[1]);
|
||||
} else {
|
||||
trade.partnerVanityURL = profileLink.match(/\/([^\/]+)$/)[1];
|
||||
if (options.resolveVanityURLs && vanityURLs.indexOf(trade.partnerVanityURL) == -1) {
|
||||
vanityURLs.push(trade.partnerVanityURL);
|
||||
}
|
||||
}
|
||||
|
||||
items = item.find('.history_item');
|
||||
for (j = 0; j < items.length; j++) {
|
||||
match = body.match(new RegExp("HistoryPageCreateItemHover\\( '" + $(items[j]).attr('id') + "', (\\d+), '(\\d+)', '(\\d+|class_\\d+_instance_\\d+|class_\\d+)', '(\\d+)' \\);"));
|
||||
econItem = historyInventory[match[1]][match[2]][match[3]];
|
||||
|
||||
if ($(items[j]).attr('id').indexOf('received') != -1) {
|
||||
trade.itemsReceived.push(new CEconItem(econItem));
|
||||
} else {
|
||||
trade.itemsGiven.push(new CEconItem(econItem));
|
||||
}
|
||||
}
|
||||
|
||||
output.trades.push(trade);
|
||||
}
|
||||
|
||||
if (options.resolveVanityURLs) {
|
||||
Async.map(vanityURLs, Helpers.resolveVanityURL, function(err, results) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < output.trades.length; i++) {
|
||||
if (output.trades[i].partnerSteamID || !output.trades[i].partnerVanityURL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the vanity URL
|
||||
for (j = 0; j < results.length; j++) {
|
||||
if (results[j].vanityURL == output.trades[i].partnerVanityURL) {
|
||||
output.trades[i].partnerSteamID = new SteamID(results[j].steamID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, output);
|
||||
});
|
||||
} else {
|
||||
callback(null, output);
|
||||
}
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
const {chrome} = require('@doctormckay/user-agents');
|
||||
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
/**
|
||||
* @typedef LogOnDetails
|
||||
* @property {string} accountName
|
||||
* @property {string} password
|
||||
* @property {string} [steamguard]
|
||||
* @property {string} [authCode]
|
||||
* @property {string} [twoFactorCode]
|
||||
* @property {boolean} disableMobile
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef LogOnResponse
|
||||
* @property {string} sessionID
|
||||
* @property {string[]} cookies
|
||||
* @property {string} steamguard
|
||||
* @property {string} [mobileAccessToken]
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {LogOnDetails} logOnDetails
|
||||
* @returns {Promise<LogOnResponse>}
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._modernLogin = function(logOnDetails) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (!isNodeVersionNewEnough()) {
|
||||
return reject(new Error(`Node.js version is too old! Need >=12.22.0 or later, got ${process.versions.node}.`));
|
||||
}
|
||||
|
||||
if (this._options.request) {
|
||||
return reject(new Error('SteamCommunity.login() is incompatible with node-steamcommunity v3\'s usage of \'request\'. If you need to specify a custom \'request\' instance (e.g. when using a proxy), use https://www.npmjs.com/package/steam-session directly to log onto Steam.'));
|
||||
}
|
||||
|
||||
// Import this here so we don't cause problems on old Node versions if this code path isn't taken.
|
||||
const {LoginSession, EAuthTokenPlatformType, EAuthSessionGuardType} = require('steam-session');
|
||||
|
||||
let session = new LoginSession(
|
||||
logOnDetails.disableMobile
|
||||
? EAuthTokenPlatformType.WebBrowser
|
||||
: EAuthTokenPlatformType.MobileApp,
|
||||
{
|
||||
localAddress: this._options.localAddress,
|
||||
userAgent: this._options.userAgent || chrome()
|
||||
}
|
||||
);
|
||||
|
||||
session.on('authenticated', async () => {
|
||||
try {
|
||||
let webCookies = await session.getWebCookies();
|
||||
let sessionIdCookie = webCookies.find(c => c.startsWith('sessionid='));
|
||||
resolve({
|
||||
sessionID: sessionIdCookie.split('=')[1].split(';')[0].trim(),
|
||||
cookies: webCookies,
|
||||
steamguard: session.steamGuardMachineToken,
|
||||
mobileAccessToken: logOnDetails.disableMobile ? null : session.accessToken
|
||||
});
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
|
||||
session.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
try {
|
||||
let startResult = await session.startWithCredentials({
|
||||
accountName: logOnDetails.accountName,
|
||||
password: logOnDetails.password,
|
||||
steamGuardMachineToken: logOnDetails.steamguard,
|
||||
steamGuardCode: logOnDetails.authCode || logOnDetails.twoFactorCode
|
||||
});
|
||||
|
||||
if (startResult.actionRequired) {
|
||||
// Cannot continue with login, need something from the user
|
||||
session.cancelLoginAttempt();
|
||||
|
||||
let emailMfaAction = startResult.validActions.find(action => action.type == EAuthSessionGuardType.EmailCode);
|
||||
if (emailMfaAction) {
|
||||
let err = new Error('SteamGuard');
|
||||
err.emaildomain = emailMfaAction.detail;
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
return reject(new Error('SteamGuardMobile'));
|
||||
}
|
||||
} catch (ex) {
|
||||
return reject(ex);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function isNodeVersionNewEnough() {
|
||||
let [major, minor] = process.versions.node.split('.');
|
||||
|
||||
if (major < 12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (major == 12 && minor < 22) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
const SteamCommunity = require('../index.js');
|
||||
const Cheerio = require('cheerio');
|
||||
|
||||
const SteamCommunity = require('../index.js');
|
||||
const Helpers = require('./helpers.js');
|
||||
|
||||
/**
|
||||
@@ -8,28 +8,27 @@ const Helpers = require('./helpers.js');
|
||||
* @param {function} callback - First argument is null|Error, second is an object of appid => name
|
||||
*/
|
||||
SteamCommunity.prototype.getMarketApps = function(callback) {
|
||||
var self = this;
|
||||
this.httpRequest('https://steamcommunity.com/market/', function (err, response, body) {
|
||||
this.httpRequest('https://steamcommunity.com/market/', (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body);
|
||||
let $ = Cheerio.load(body);
|
||||
if ($('.market_search_game_button_group')) {
|
||||
let apps = {};
|
||||
$('.market_search_game_button_group a.game_button').each(function (i, element) {
|
||||
var e = Cheerio.load(element);
|
||||
var name = e('.game_button_game_name').text().trim();
|
||||
var url = element.attribs.href;
|
||||
var appid = url.substr(url.indexOf('=') + 1);
|
||||
$('.market_search_game_button_group a.game_button').each((i, element) => {
|
||||
let e = Cheerio.load(element);
|
||||
let name = e('.game_button_game_name').text().trim();
|
||||
let url = element.attribs.href;
|
||||
let appid = url.substr(url.indexOf('=') + 1);
|
||||
apps[appid] = name;
|
||||
});
|
||||
callback(null, apps);
|
||||
} else {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
}
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -40,34 +39,32 @@ SteamCommunity.prototype.getMarketApps = function(callback) {
|
||||
*/
|
||||
SteamCommunity.prototype.getGemValue = function(appid, assetid, callback) {
|
||||
this._myProfile({
|
||||
"endpoint": "ajaxgetgoovalue/",
|
||||
"qs": {
|
||||
"sessionid": this.getSessionID(),
|
||||
"appid": appid,
|
||||
"contextid": 6,
|
||||
"assetid": assetid
|
||||
endpoint: 'ajaxgetgoovalue/',
|
||||
qs: {
|
||||
sessionid: this.getSessionID(),
|
||||
appid: appid,
|
||||
contextid: 6,
|
||||
assetid: assetid
|
||||
},
|
||||
"checkHttpError": false,
|
||||
"json": true
|
||||
checkHttpError: false,
|
||||
json: true
|
||||
}, null, (err, res, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success && body.success != SteamCommunity.EResult.OK) {
|
||||
let err = new Error(body.message || SteamCommunity.EResult[body.success]);
|
||||
err.eresult = err.code = body.success;
|
||||
callback(err);
|
||||
return;
|
||||
let err2 = Helpers.eresultError(body.success, body.message);
|
||||
if (err2) {
|
||||
return callback(err2);
|
||||
}
|
||||
|
||||
if (!body.goo_value || !body.strTitle) {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {"promptTitle": body.strTitle, "gemValue": parseInt(body.goo_value, 10)});
|
||||
callback(null, {promptTitle: body.strTitle, gemValue: parseInt(body.goo_value, 10)});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -80,35 +77,33 @@ SteamCommunity.prototype.getGemValue = function(appid, assetid, callback) {
|
||||
*/
|
||||
SteamCommunity.prototype.turnItemIntoGems = function(appid, assetid, expectedGemsValue, callback) {
|
||||
this._myProfile({
|
||||
"endpoint": "ajaxgrindintogoo/",
|
||||
"json": true,
|
||||
"checkHttpError": false
|
||||
endpoint: 'ajaxgrindintogoo/',
|
||||
json: true,
|
||||
checkHttpError: false
|
||||
}, {
|
||||
"appid": appid,
|
||||
"contextid": 6,
|
||||
"assetid": assetid,
|
||||
"goo_value_expected": expectedGemsValue,
|
||||
"sessionid": this.getSessionID()
|
||||
appid: appid,
|
||||
contextid: 6,
|
||||
assetid: assetid,
|
||||
goo_value_expected: expectedGemsValue,
|
||||
sessionid: this.getSessionID()
|
||||
}, (err, res, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success && body.success != SteamCommunity.EResult.OK) {
|
||||
let err = new Error(body.message || SteamCommunity.EResult[body.success]);
|
||||
err.eresult = err.code = body.success;
|
||||
callback(err);
|
||||
let err2 = Helpers.eresultError(body.success, body.message);
|
||||
if (err2) {
|
||||
return callback(err2);
|
||||
}
|
||||
|
||||
if (!body['goo_value_received '] || !body.goo_value_total) { // lol valve, that trailing space is real
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!body['goo_value_received '] || !body.goo_value_total) { // lol valve
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {"gemsReceived": parseInt(body['goo_value_received '], 10), "totalGems": parseInt(body.goo_value_total, 10)});
|
||||
})
|
||||
callback(null, {gemsReceived: parseInt(body['goo_value_received '], 10), totalGems: parseInt(body.goo_value_total, 10)});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -119,33 +114,31 @@ SteamCommunity.prototype.turnItemIntoGems = function(appid, assetid, expectedGem
|
||||
*/
|
||||
SteamCommunity.prototype.openBoosterPack = function(appid, assetid, callback) {
|
||||
this._myProfile({
|
||||
"endpoint": "ajaxunpackbooster/",
|
||||
"json": true,
|
||||
"checkHttpError": false
|
||||
endpoint: 'ajaxunpackbooster/',
|
||||
json: true,
|
||||
checkHttpError: false
|
||||
}, {
|
||||
"appid": appid,
|
||||
"communityitemid": assetid,
|
||||
"sessionid": this.getSessionID()
|
||||
appid: appid,
|
||||
communityitemid: assetid,
|
||||
sessionid: this.getSessionID()
|
||||
}, (err, res, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success && body.success != SteamCommunity.EResult.OK) {
|
||||
let err = new Error(body.message || SteamCommunity.EResult[body.success]);
|
||||
err.eresult = err.code = body.success;
|
||||
callback(err);
|
||||
return;
|
||||
let err2 = Helpers.eresultError(body.success, body.message);
|
||||
if (err2) {
|
||||
return callback(err2);
|
||||
}
|
||||
|
||||
if (!body.rgItems) {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, body.rgItems);
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -228,7 +221,7 @@ SteamCommunity.prototype.createBoosterPack = function(appid, useUntradableGems,
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
uri: 'https://steamcommunity.com/tradingcards/ajaxcreatebooster/',
|
||||
url: 'https://steamcommunity.com/tradingcards/ajaxcreatebooster/',
|
||||
form: {
|
||||
sessionid: this.getSessionID(),
|
||||
appid,
|
||||
@@ -254,6 +247,7 @@ SteamCommunity.prototype.createBoosterPack = function(appid, useUntradableGems,
|
||||
|
||||
// We can now check HTTP status codes
|
||||
if (this._checkHttpError(err, res, callback, body)) {
|
||||
// TODO v4
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -273,33 +267,31 @@ SteamCommunity.prototype.createBoosterPack = function(appid, useUntradableGems,
|
||||
*/
|
||||
SteamCommunity.prototype.getGiftDetails = function(giftID, callback) {
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/gifts/" + giftID + "/validateunpack",
|
||||
"form": {
|
||||
"sessionid": this.getSessionID()
|
||||
url: `https://steamcommunity.com/gifts/${giftID}/validateunpack`,
|
||||
form: {
|
||||
sessionid: this.getSessionID()
|
||||
},
|
||||
"json": true
|
||||
json: true
|
||||
}, (err, res, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success && body.success != SteamCommunity.EResult.OK) {
|
||||
let err = new Error(body.message || SteamCommunity.EResult[body.success]);
|
||||
err.eresult = err.code = body.success;
|
||||
callback(err);
|
||||
return;
|
||||
let err2 = Helpers.eresultError(body.success, body.message);
|
||||
if (err2) {
|
||||
return callback(err2);
|
||||
}
|
||||
|
||||
if (!body.packageid || !body.gift_name) {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
"giftName": body.gift_name,
|
||||
"packageID": parseInt(body.packageid, 10),
|
||||
"owned": body.owned
|
||||
giftName: body.gift_name,
|
||||
packageID: parseInt(body.packageid, 10),
|
||||
owned: body.owned
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -311,23 +303,20 @@ SteamCommunity.prototype.getGiftDetails = function(giftID, callback) {
|
||||
*/
|
||||
SteamCommunity.prototype.redeemGift = function(giftID, callback) {
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/gifts/" + giftID + "/unpack",
|
||||
"form": {
|
||||
"sessionid": this.getSessionID()
|
||||
url: `https://steamcommunity.com/gifts/${giftID}/unpack`,
|
||||
form: {
|
||||
sessionid: this.getSessionID()
|
||||
},
|
||||
"json": true
|
||||
json: true
|
||||
}, (err, res, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success && body.success != SteamCommunity.EResult.OK) {
|
||||
|
||||
let err = new Error(body.message || SteamCommunity.EResult[body.success]);
|
||||
err.eresult = err.code = body.success;
|
||||
callback(err);
|
||||
return;
|
||||
let err2 = Helpers.eresultError(body.success, body.message);
|
||||
if (err2) {
|
||||
return callback(err2);
|
||||
}
|
||||
|
||||
callback(null);
|
||||
|
||||
@@ -1,40 +1,46 @@
|
||||
const Cheerio = require('cheerio');
|
||||
const FS = require('fs');
|
||||
const SteamID = require('steamid');
|
||||
|
||||
const Helpers = require('./helpers.js');
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
SteamCommunity.PrivacyState = {
|
||||
"Private": 1,
|
||||
"FriendsOnly": 2,
|
||||
"Public": 3
|
||||
Private: 1,
|
||||
FriendsOnly: 2,
|
||||
Public: 3
|
||||
};
|
||||
|
||||
var CommentPrivacyState = {
|
||||
"1": 2, // private
|
||||
"2": 0, // friends only
|
||||
"3": 1 // anyone
|
||||
const CommentPrivacyState = {
|
||||
1: 2, // private
|
||||
2: 0, // friends only
|
||||
3: 1 // anyone
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a profile page if you don't already have one.
|
||||
* @param {function} callback
|
||||
*/
|
||||
SteamCommunity.prototype.setupProfile = function(callback) {
|
||||
var self = this;
|
||||
this._myProfile("edit?welcomed=1", null, function(err, response, body) {
|
||||
if(!callback) {
|
||||
this._myProfile('edit?welcomed=1', null, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode != 200) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
if (err || response.statusCode != 200) {
|
||||
callback(err || new Error('HTTP error ' + response.statusCode));
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits your profile details.
|
||||
* @param {object} settings
|
||||
* @param {function} callback
|
||||
*/
|
||||
SteamCommunity.prototype.editProfile = function(settings, callback) {
|
||||
var self = this;
|
||||
this._myProfile('edit/info', null, function(err, response, body) {
|
||||
this._myProfile('edit/info', null, (err, response, body) => {
|
||||
if (err || response.statusCode != 200) {
|
||||
if (callback) {
|
||||
callback(err || new Error('HTTP error ' + response.statusCode));
|
||||
@@ -43,8 +49,8 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body);
|
||||
var existingSettings = $('#profile_edit_config').data('profile-edit');
|
||||
let $ = Cheerio.load(body);
|
||||
let existingSettings = $('#profile_edit_config').data('profile-edit');
|
||||
if (!existingSettings || !existingSettings.strPersonaName) {
|
||||
if (callback) {
|
||||
callback(new Error('Malformed response'));
|
||||
@@ -53,8 +59,8 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
var values = {
|
||||
sessionID: self.getSessionID(),
|
||||
let values = {
|
||||
sessionID: this.getSessionID(),
|
||||
type: 'profileSave',
|
||||
weblink_1_title: '',
|
||||
weblink_1_url: '',
|
||||
@@ -72,12 +78,8 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
|
||||
json: 1
|
||||
};
|
||||
|
||||
for (var i in settings) {
|
||||
if(!settings.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
for (let i in settings) {
|
||||
switch (i) {
|
||||
case 'name':
|
||||
values.personaName = settings[i];
|
||||
break;
|
||||
@@ -131,9 +133,9 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
self._myProfile('edit', values, function(err, response, body) {
|
||||
this._myProfile('edit', values, (err, response, body) => {
|
||||
if (settings.customURL) {
|
||||
delete self._profileURL;
|
||||
delete this._profileURL;
|
||||
}
|
||||
|
||||
if (!callback) {
|
||||
@@ -146,10 +148,10 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
|
||||
}
|
||||
|
||||
try {
|
||||
var json = JSON.parse(body);
|
||||
if (!json.success || json.success != 1) {
|
||||
callback(new Error(json.errmsg || 'Request was not successful'));
|
||||
return;
|
||||
let json = JSON.parse(body);
|
||||
let err2 = Helpers.eresultError(json.success, json.errmsg);
|
||||
if (err2) {
|
||||
return callback(err2);
|
||||
}
|
||||
|
||||
callback(null);
|
||||
@@ -170,8 +172,8 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body);
|
||||
var existingSettings = $('#profile_edit_config').data('profile-edit');
|
||||
let $ = Cheerio.load(body);
|
||||
let existingSettings = $('#profile_edit_config').data('profile-edit');
|
||||
if (!existingSettings || !existingSettings.Privacy) {
|
||||
if (callback) {
|
||||
callback(new Error('Malformed response'));
|
||||
@@ -182,14 +184,10 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
|
||||
|
||||
// PrivacySettings => {PrivacyProfile, PrivacyInventory, PrivacyInventoryGifts, PrivacyOwnedGames, PrivacyPlaytime}
|
||||
// eCommentPermission
|
||||
var privacy = existingSettings.Privacy.PrivacySettings;
|
||||
var commentPermission = existingSettings.Privacy.eCommentPermission;
|
||||
|
||||
for (var i in settings) {
|
||||
if (!settings.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
let privacy = existingSettings.Privacy.PrivacySettings;
|
||||
let commentPermission = existingSettings.Privacy.eCommentPermission;
|
||||
|
||||
for (let i in settings) {
|
||||
switch (i) {
|
||||
case 'profile':
|
||||
privacy.PrivacyProfile = settings[i];
|
||||
@@ -230,7 +228,7 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
|
||||
Privacy: JSON.stringify(privacy),
|
||||
eCommentPermission: commentPermission
|
||||
}
|
||||
}, null, function(err, response, body) {
|
||||
}, null, (err, response, body) => {
|
||||
if (err || response.statusCode != 200) {
|
||||
if (callback) {
|
||||
callback(err || new Error('HTTP error ' + response.statusCode));
|
||||
@@ -239,11 +237,9 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success != 1) {
|
||||
if (callback) {
|
||||
callback(new Error(body.success ? 'Error ' + body.success : 'Request was not successful'));
|
||||
}
|
||||
|
||||
let err2 = Helpers.eresultError(body.success);
|
||||
if (err2) {
|
||||
callback && callback(err2);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -255,78 +251,34 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.uploadAvatar = function(image, format, callback) {
|
||||
if(typeof format === 'function') {
|
||||
if (typeof format === 'function') {
|
||||
callback = format;
|
||||
format = null;
|
||||
}
|
||||
|
||||
// are we logged in?
|
||||
if (!this.steamID) {
|
||||
callback(new Error("Not Logged In"));
|
||||
callback(new Error('Not Logged In'));
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
if(image instanceof Buffer) {
|
||||
doUpload(image);
|
||||
} else if(image.match(/^https?:\/\//)) {
|
||||
this.httpRequestGet({
|
||||
"uri": image,
|
||||
"encoding": null
|
||||
}, function(err, response, body) {
|
||||
if(err || response.statusCode != 200) {
|
||||
if(callback) {
|
||||
callback(err ? new Error(err.message + " downloading image") : new Error("HTTP error " + response.statusCode + " downloading image"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!format) {
|
||||
format = response.headers['content-type'];
|
||||
}
|
||||
|
||||
doUpload(body);
|
||||
}, "steamcommunity");
|
||||
} else {
|
||||
if(!format) {
|
||||
format = image.match(/\.([^\.]+)$/);
|
||||
if(format) {
|
||||
format = format[1];
|
||||
}
|
||||
}
|
||||
|
||||
FS.readFile(image, function(err, file) {
|
||||
if(err) {
|
||||
if(callback) {
|
||||
callback(err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
doUpload(file);
|
||||
})
|
||||
}
|
||||
|
||||
function doUpload(buffer) {
|
||||
if(!format) {
|
||||
if(callback) {
|
||||
callback(new Error("Unknown image format"));
|
||||
const doUpload = (buffer) => {
|
||||
if (!format) {
|
||||
if (callback) {
|
||||
callback(new Error('Unknown image format'));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(format.match(/^image\//)) {
|
||||
if (format.match(/^image\//)) {
|
||||
format = format.substring(6);
|
||||
}
|
||||
|
||||
var filename = '';
|
||||
var contentType = '';
|
||||
let filename = '';
|
||||
let contentType = '';
|
||||
|
||||
switch(format.toLowerCase()) {
|
||||
switch (format.toLowerCase()) {
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
filename = 'avatar.jpg';
|
||||
@@ -344,68 +296,96 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) {
|
||||
break;
|
||||
|
||||
default:
|
||||
if(callback) {
|
||||
callback(new Error("Unknown or invalid image format"));
|
||||
if (callback) {
|
||||
callback(new Error('Unknown or invalid image format'));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/actions/FileUploader",
|
||||
"formData": {
|
||||
"MAX_FILE_SIZE": buffer.length,
|
||||
"type": "player_avatar_image",
|
||||
"sId": self.steamID.getSteamID64(),
|
||||
"sessionid": self.getSessionID(),
|
||||
"doSub": 1,
|
||||
"json": 1,
|
||||
"avatar": {
|
||||
"value": buffer,
|
||||
"options": {
|
||||
"filename": filename,
|
||||
"contentType": contentType
|
||||
this.httpRequestPost({
|
||||
url: 'https://steamcommunity.com/actions/FileUploader',
|
||||
formData: {
|
||||
MAX_FILE_SIZE: buffer.length,
|
||||
type: 'player_avatar_image',
|
||||
sId: this.steamID.getSteamID64(),
|
||||
sessionid: this.getSessionID(),
|
||||
doSub: 1,
|
||||
json: 1,
|
||||
avatar: {
|
||||
value: buffer,
|
||||
options: {
|
||||
filename: filename,
|
||||
contentType: contentType
|
||||
}
|
||||
}
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(err) {
|
||||
if(callback) {
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
callback && callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body && !body.success && body.message) {
|
||||
callback && callback(new Error(body.message));
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
callback && callback(new Error(`HTTP error ${response.statusCode}`));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!body || !body.success) {
|
||||
callback && callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback && callback(null, body.images.full);
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
if (image instanceof Buffer) {
|
||||
doUpload(image);
|
||||
} else if (image.match(/^https?:\/\//)) {
|
||||
this.httpRequestGet({
|
||||
url: image,
|
||||
encoding: null
|
||||
}, (err, response, body) => {
|
||||
if (err || response.statusCode != 200) {
|
||||
if (callback) {
|
||||
callback(new Error(err ? `${err.message} downloading image` : `HTTP error ${response.statusCode} downloading image`));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!format) {
|
||||
format = response.headers['content-type'];
|
||||
}
|
||||
|
||||
doUpload(body);
|
||||
}, 'steamcommunity');
|
||||
} else {
|
||||
if (!format) {
|
||||
format = image.match(/\.([^.]+)$/);
|
||||
if (format) {
|
||||
format = format[1];
|
||||
}
|
||||
}
|
||||
|
||||
FS.readFile(image, (err, file) => {
|
||||
if (err) {
|
||||
if (callback) {
|
||||
callback(err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(body && !body.success && body.message) {
|
||||
if(callback) {
|
||||
callback(new Error(body.message));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(response.statusCode != 200) {
|
||||
if(callback) {
|
||||
callback(new Error("HTTP error " + response.statusCode));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body || !body.success) {
|
||||
if(callback) {
|
||||
callback(new Error("Malformed response"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(callback) {
|
||||
callback(null, body.images.full);
|
||||
}
|
||||
}, "steamcommunity");
|
||||
doUpload(file);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -421,10 +401,10 @@ SteamCommunity.prototype.postProfileStatus = function(statusText, options, callb
|
||||
options = {};
|
||||
}
|
||||
|
||||
this._myProfile("ajaxpostuserstatus/", {
|
||||
"appid": options.appID || 0,
|
||||
"sessionid": this.getSessionID(),
|
||||
"status_text": statusText
|
||||
this._myProfile('ajaxpostuserstatus/', {
|
||||
appid: options.appID || 0,
|
||||
sessionid: this.getSessionID(),
|
||||
status_text: statusText
|
||||
}, (err, res, body) => {
|
||||
try {
|
||||
body = JSON.parse(body);
|
||||
@@ -433,9 +413,9 @@ SteamCommunity.prototype.postProfileStatus = function(statusText, options, callb
|
||||
return;
|
||||
}
|
||||
|
||||
var match = body.blotter_html.match(/id="userstatus_(\d+)_/);
|
||||
let match = body.blotter_html.match(/id="userstatus_(\d+)_/);
|
||||
if (!match) {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -452,9 +432,9 @@ SteamCommunity.prototype.postProfileStatus = function(statusText, options, callb
|
||||
* @param {function} [callback]
|
||||
*/
|
||||
SteamCommunity.prototype.deleteProfileStatus = function(postID, callback) {
|
||||
this._myProfile("ajaxdeleteuserstatus/", {
|
||||
"sessionid": this.getSessionID(),
|
||||
"postid": postID
|
||||
this._myProfile('ajaxdeleteuserstatus/', {
|
||||
sessionid: this.getSessionID(),
|
||||
postid: postID
|
||||
}, (err, res, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
@@ -463,7 +443,7 @@ SteamCommunity.prototype.deleteProfileStatus = function(postID, callback) {
|
||||
try {
|
||||
body = JSON.parse(body);
|
||||
if (!body.success) {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var SteamID = require('steamid');
|
||||
const StdLib = require('@doctormckay/stdlib');
|
||||
const SteamID = require('steamid');
|
||||
|
||||
var SteamCommunity = require('../index.js');
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
|
||||
/**
|
||||
@@ -8,50 +9,52 @@ var SteamCommunity = require('../index.js');
|
||||
* @param {SteamID | String} userID - ID of the user associated to this sharedfile
|
||||
* @param {String} sharedFileId - ID of the sharedfile
|
||||
* @param {String} cid - ID of the comment to delete
|
||||
* @param {function} callback - Takes only an Error object/null as the first argument
|
||||
* @param {function} [callback] - Takes only an Error object/null as the first argument
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.deleteSharedFileComment = function(userID, sharedFileId, cid, callback) {
|
||||
if (typeof userID === "string") {
|
||||
if (typeof userID == 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
"uri": `https://steamcommunity.com/comment/PublishedFile_Public/delete/${userID.toString()}/${sharedFileId}/`,
|
||||
"form": {
|
||||
"gidcomment": cid,
|
||||
"count": 10,
|
||||
"sessionid": this.getSessionID()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: `https://steamcommunity.com/comment/PublishedFile_Public/delete/${userID.toString()}/${sharedFileId}/`,
|
||||
form: {
|
||||
gidcomment: cid,
|
||||
count: 10,
|
||||
sessionid: this.getSessionID()
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
callback(err);
|
||||
}, "steamcommunity");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Favorites a sharedfile
|
||||
* @param {String} sharedFileId - ID of the sharedfile
|
||||
* @param {String} appid - ID of the app associated to this sharedfile
|
||||
* @param {function} callback - Takes only an Error object/null as the first argument
|
||||
* @param {function} [callback] - Takes only an Error object/null as the first argument
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.favoriteSharedFile = function(sharedFileId, appid, callback) {
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/sharedfiles/favorite",
|
||||
"form": {
|
||||
"id": sharedFileId,
|
||||
"appid": appid,
|
||||
"sessionid": this.getSessionID()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: 'https://steamcommunity.com/sharedfiles/favorite',
|
||||
form: {
|
||||
id: sharedFileId,
|
||||
appid,
|
||||
sessionid: this.getSessionID()
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
callback(err);
|
||||
}, "steamcommunity");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -59,76 +62,79 @@ SteamCommunity.prototype.favoriteSharedFile = function(sharedFileId, appid, call
|
||||
* @param {SteamID | String} userID - ID of the user associated to this sharedfile
|
||||
* @param {String} sharedFileId - ID of the sharedfile
|
||||
* @param {String} message - Content of the comment to post
|
||||
* @param {function} callback - Takes only an Error object/null as the first argument
|
||||
* @param {function} [callback] - Takes only an Error object/null as the first argument
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.postSharedFileComment = function(userID, sharedFileId, message, callback) {
|
||||
if (typeof userID === "string") {
|
||||
if (typeof userID == 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
"uri": `https://steamcommunity.com/comment/PublishedFile_Public/post/${userID.toString()}/${sharedFileId}/`,
|
||||
"form": {
|
||||
"comment": message,
|
||||
"count": 10,
|
||||
"sessionid": this.getSessionID()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: `https://steamcommunity.com/comment/PublishedFile_Public/post/${userID.toString()}/${sharedFileId}/`,
|
||||
form: {
|
||||
comment: message,
|
||||
count: 10,
|
||||
sessionid: this.getSessionID()
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
callback(err);
|
||||
}, "steamcommunity");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Subscribes to a sharedfile's comment section. Note: Checkbox on webpage does not update
|
||||
* @param {SteamID | String} userID ID of the user associated to this sharedfile
|
||||
* @param {String} sharedFileId ID of the sharedfile
|
||||
* @param {function} callback - Takes only an Error object/null as the first argument
|
||||
* @param {function} [callback] - Takes only an Error object/null as the first argument
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.subscribeSharedFileComments = function(userID, sharedFileId, callback) {
|
||||
if (typeof userID === "string") {
|
||||
if (typeof userID == 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
"uri": `https://steamcommunity.com/comment/PublishedFile_Public/subscribe/${userID.toString()}/${sharedFileId}/`,
|
||||
"form": {
|
||||
"count": 10,
|
||||
"sessionid": this.getSessionID()
|
||||
}
|
||||
}, function(err, response, body) { // eslint-disable-line
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: `https://steamcommunity.com/comment/PublishedFile_Public/subscribe/${userID.toString()}/${sharedFileId}/`,
|
||||
form: {
|
||||
count: 10,
|
||||
sessionid: this.getSessionID()
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
callback(err);
|
||||
}, "steamcommunity");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Unfavorites a sharedfile
|
||||
* @param {String} sharedFileId - ID of the sharedfile
|
||||
* @param {String} appid - ID of the app associated to this sharedfile
|
||||
* @param {function} callback - Takes only an Error object/null as the first argument
|
||||
* @param {function} [callback] - Takes only an Error object/null as the first argument
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.unfavoriteSharedFile = function(sharedFileId, appid, callback) {
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/sharedfiles/unfavorite",
|
||||
"form": {
|
||||
"id": sharedFileId,
|
||||
"appid": appid,
|
||||
"sessionid": this.getSessionID()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: 'https://steamcommunity.com/sharedfiles/unfavorite',
|
||||
form: {
|
||||
id: sharedFileId,
|
||||
appid,
|
||||
sessionid: this.getSessionID()
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
callback(err);
|
||||
}, "steamcommunity");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -138,21 +144,20 @@ SteamCommunity.prototype.unfavoriteSharedFile = function(sharedFileId, appid, ca
|
||||
* @param {function} callback - Takes only an Error object/null as the first argument
|
||||
*/
|
||||
SteamCommunity.prototype.unsubscribeSharedFileComments = function(userID, sharedFileId, callback) {
|
||||
if (typeof userID === "string") {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
"uri": `https://steamcommunity.com/comment/PublishedFile_Public/unsubscribe/${userID.toString()}/${sharedFileId}/`,
|
||||
"form": {
|
||||
"count": 10,
|
||||
"sessionid": this.getSessionID()
|
||||
}
|
||||
}, function(err, response, body) { // eslint-disable-line
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: `https://steamcommunity.com/comment/PublishedFile_Public/unsubscribe/${userID.toString()}/${sharedFileId}/`,
|
||||
form: {
|
||||
count: 10,
|
||||
sessionid: this.getSessionID()
|
||||
}
|
||||
});
|
||||
|
||||
callback(err);
|
||||
}, "steamcommunity");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,152 +1,164 @@
|
||||
var SteamTotp = require('steam-totp');
|
||||
var SteamCommunity = require('../index.js');
|
||||
const StdLib = require('@doctormckay/stdlib');
|
||||
const SteamTotp = require('steam-totp');
|
||||
|
||||
var ETwoFactorTokenType = {
|
||||
const SteamCommunity = require('../index.js');
|
||||
const Helpers = require('./helpers.js');
|
||||
|
||||
const ETwoFactorTokenType = {
|
||||
None: 0, // No token-based two-factor authentication
|
||||
ValveMobileApp: 1, // Tokens generated using Valve's special charset (5 digits, alphanumeric)
|
||||
ThirdParty: 2 // Tokens generated using literally everyone else's standard charset (6 digits, numeric). This is disabled.
|
||||
ThirdParty: 2 // Tokens generated using literally everyone else's standard charset (6 digits, numeric). This is disabled on the backend.
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {function} [callback]
|
||||
* @return {Promise<object>}
|
||||
*/
|
||||
SteamCommunity.prototype.enableTwoFactor = function(callback) {
|
||||
this._verifyMobileAccessToken();
|
||||
return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => {
|
||||
this._verifyMobileAccessToken();
|
||||
|
||||
if (!this.mobileAccessToken) {
|
||||
callback(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
uri: "https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/?access_token=" + this.mobileAccessToken,
|
||||
// TODO: Send this as protobuf to more closely mimic official app behavior
|
||||
form: {
|
||||
steamid: this.steamID.getSteamID64(),
|
||||
authenticator_type: ETwoFactorTokenType.ValveMobileApp,
|
||||
device_identifier: SteamTotp.getDeviceID(this.steamID),
|
||||
sms_phone_id: '1',
|
||||
version: 2
|
||||
},
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
if (!this.mobileAccessToken) {
|
||||
return reject(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()'));
|
||||
}
|
||||
|
||||
if (!body.response) {
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.response.status != 1) {
|
||||
var error = new Error('Error ' + body.response.status);
|
||||
error.eresult = body.response.status;
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, body.response);
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, callback) {
|
||||
this._verifyMobileAccessToken();
|
||||
|
||||
if (!this.mobileAccessToken) {
|
||||
callback(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()'));
|
||||
return;
|
||||
}
|
||||
|
||||
let attemptsLeft = 30;
|
||||
let diff = 0;
|
||||
|
||||
let finalize = () => {
|
||||
let code = SteamTotp.generateAuthCode(secret, diff);
|
||||
|
||||
this.httpRequestPost({
|
||||
uri: 'https://api.steampowered.com/ITwoFactorService/FinalizeAddAuthenticator/v1/?access_token=' + this.mobileAccessToken,
|
||||
let {jsonBody} = await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: `https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/?access_token=${this.mobileAccessToken}`,
|
||||
// TODO: Send this as protobuf to more closely mimic official app behavior
|
||||
form: {
|
||||
steamid: this.steamID.getSteamID64(),
|
||||
authenticator_code: code,
|
||||
authenticator_time: Math.floor(Date.now() / 1000),
|
||||
activation_code: activationCode
|
||||
authenticator_type: ETwoFactorTokenType.ValveMobileApp,
|
||||
device_identifier: SteamTotp.getDeviceID(this.steamID),
|
||||
sms_phone_id: '1'
|
||||
},
|
||||
json: true
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
|
||||
if (!jsonBody.response) {
|
||||
return reject(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
if (jsonBody.response.status != 1) {
|
||||
let error = new Error(`Error ${jsonBody.response.status}`);
|
||||
error.eresult = jsonBody.response.status;
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve(jsonBody.response);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} secret
|
||||
* @param {string} activationCode
|
||||
* @param {function} [callback]
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, callback) {
|
||||
return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => {
|
||||
this._verifyMobileAccessToken();
|
||||
|
||||
if (!this.mobileAccessToken) {
|
||||
return reject(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()'));
|
||||
}
|
||||
|
||||
let attemptsLeft = 30;
|
||||
let diff = 0;
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
SteamTotp.getTimeOffset(function(err, offset, latency) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
diff = offset;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
let finalize = async () => {
|
||||
let code = SteamTotp.generateAuthCode(secret, diff);
|
||||
|
||||
let {jsonBody} = this.httpRequest({
|
||||
method: 'POST',
|
||||
url: `https://api.steampowered.com/ITwoFactorService/FinalizeAddAuthenticator/v1/?access_token=${this.mobileAccessToken}`,
|
||||
form: {
|
||||
steamid: this.steamID.getSteamID64(),
|
||||
authenticator_code: code,
|
||||
authenticator_time: Math.floor(Date.now() / 1000),
|
||||
activation_code: activationCode
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
if (!jsonBody.response) {
|
||||
return reject(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
if (!body.response) {
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
jsonBody = jsonBody.response;
|
||||
|
||||
if (jsonBody.server_time) {
|
||||
diff = jsonBody.server_time - Math.floor(Date.now() / 1000);
|
||||
}
|
||||
|
||||
body = body.response;
|
||||
|
||||
if (body.server_time) {
|
||||
diff = body.server_time - Math.floor(Date.now() / 1000);
|
||||
}
|
||||
|
||||
if (body.status == 89) {
|
||||
callback(new Error('Invalid activation code'));
|
||||
} else if(body.want_more) {
|
||||
attemptsLeft--;
|
||||
if (jsonBody.status == SteamCommunity.EResult.TwoFactorActivationCodeMismatch) {
|
||||
return reject(new Error('Invalid activation code'));
|
||||
} else if (jsonBody.want_more) {
|
||||
if (--attemptsLeft <= 0) {
|
||||
// We made more than 30 attempts, something must be wrong
|
||||
return reject(Helpers.eresultError(SteamCommunity.EResult.Fail));
|
||||
}
|
||||
diff += 30;
|
||||
|
||||
finalize();
|
||||
} else if(!body.success) {
|
||||
callback(new Error('Error ' + body.status));
|
||||
} else if (!jsonBody.success) {
|
||||
return reject(new Error(`Error ${jsonBody.status}`));
|
||||
} else {
|
||||
callback(null);
|
||||
resolve();
|
||||
}
|
||||
}, 'steamcommunity');
|
||||
}
|
||||
};
|
||||
|
||||
SteamTotp.getTimeOffset(function(err, offset, latency) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
diff = offset;
|
||||
finalize();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} revocationCode
|
||||
* @param {function} [callback]
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.disableTwoFactor = function(revocationCode, callback) {
|
||||
this._verifyMobileAccessToken();
|
||||
return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => {
|
||||
this._verifyMobileAccessToken();
|
||||
|
||||
if (!this.mobileAccessToken) {
|
||||
callback(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
uri: 'https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/?access_token=' + this.mobileAccessToken,
|
||||
form: {
|
||||
steamid: this.steamID.getSteamID64(),
|
||||
revocation_code: revocationCode,
|
||||
steamguard_scheme: 1
|
||||
},
|
||||
json: true
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
if (!this.mobileAccessToken) {
|
||||
callback(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!body.response) {
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
let {jsonBody} = await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: `https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/?access_token=${this.mobileAccessToken}`,
|
||||
form: {
|
||||
steamid: this.steamID.getSteamID64(),
|
||||
revocation_code: revocationCode,
|
||||
steamguard_scheme: 1
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
if (!jsonBody.response) {
|
||||
return reject(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
if (!body.response.success) {
|
||||
callback(new Error('Request failed'));
|
||||
return;
|
||||
if (!jsonBody.response.success) {
|
||||
return reject(new Error('Request failed'));
|
||||
}
|
||||
|
||||
// success = true means it worked
|
||||
callback(null);
|
||||
}, 'steamcommunity');
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -9,116 +9,111 @@ const CEconItem = require('../classes/CEconItem.js');
|
||||
const Helpers = require('./helpers.js');
|
||||
|
||||
SteamCommunity.prototype.addFriend = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/actions/AddFriendAjax",
|
||||
"form": {
|
||||
"accept_invite": 0,
|
||||
"sessionID": this.getSessionID(),
|
||||
"steamid": userID.toString()
|
||||
url: 'https://steamcommunity.com/actions/AddFriendAjax',
|
||||
form: {
|
||||
accept_invite: 0,
|
||||
sessionID: this.getSessionID(),
|
||||
steamid: userID.toString()
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if(body.success) {
|
||||
if (body.success) {
|
||||
callback(null);
|
||||
} else {
|
||||
callback(new Error("Unknown error"));
|
||||
callback(new Error('Unknown error'));
|
||||
}
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.acceptFriendRequest = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/actions/AddFriendAjax",
|
||||
"form": {
|
||||
"accept_invite": 1,
|
||||
"sessionID": this.getSessionID(),
|
||||
"steamid": userID.toString()
|
||||
url: 'https://steamcommunity.com/actions/AddFriendAjax',
|
||||
form: {
|
||||
accept_invite: 1,
|
||||
sessionID: this.getSessionID(),
|
||||
steamid: userID.toString()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
}, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(err || null);
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.removeFriend = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/actions/RemoveFriendAjax",
|
||||
"form": {
|
||||
"sessionID": this.getSessionID(),
|
||||
"steamid": userID.toString()
|
||||
url: 'https://steamcommunity.com/actions/RemoveFriendAjax',
|
||||
form: {
|
||||
sessionID: this.getSessionID(),
|
||||
steamid: userID.toString()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
}, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(err || null);
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.blockCommunication = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/actions/BlockUserAjax",
|
||||
"form": {
|
||||
"sessionID": this.getSessionID(),
|
||||
"steamid": userID.toString()
|
||||
url: 'https://steamcommunity.com/actions/BlockUserAjax',
|
||||
form: {
|
||||
sessionID: this.getSessionID(),
|
||||
steamid: userID.toString()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
}, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(err || null);
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.unblockCommunication = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var form = {"action": "unignore"};
|
||||
let form = {action: 'unignore'};
|
||||
form['friends[' + userID.toString() + ']'] = 1;
|
||||
|
||||
this._myProfile('friends/blocked/', form, function(err, response, body) {
|
||||
if(!callback) {
|
||||
this._myProfile('friends/blocked/', form, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
if (err || response.statusCode >= 400) {
|
||||
callback(err || new Error(`HTTP error ${response.statusCode}`));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,21 +122,20 @@ SteamCommunity.prototype.unblockCommunication = function(userID, callback) {
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.postUserComment = function(userID, message, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/comment/Profile/post/" + userID.toString() + "/-1",
|
||||
"form": {
|
||||
"comment": message,
|
||||
"count": 1,
|
||||
"sessionid": this.getSessionID()
|
||||
url: `https://steamcommunity.com/comment/Profile/post/${userID.toString()}/-1`,
|
||||
form: {
|
||||
comment: message,
|
||||
count: 1,
|
||||
sessionid: this.getSessionID()
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -150,37 +144,36 @@ SteamCommunity.prototype.postUserComment = function(userID, message, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.success) {
|
||||
if (body.success) {
|
||||
const $ = Cheerio.load(body.comments_html);
|
||||
const commentID = $('.commentthread_comment').attr('id').split('_')[1];
|
||||
|
||||
callback(null, commentID);
|
||||
} else if(body.error) {
|
||||
} else if (body.error) {
|
||||
callback(new Error(body.error));
|
||||
} else {
|
||||
callback(new Error("Unknown error"));
|
||||
callback(new Error('Unknown error'));
|
||||
}
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.deleteUserComment = function(userID, commentID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/comment/Profile/delete/" + userID.toString() + "/-1",
|
||||
"form": {
|
||||
"gidcomment": commentID,
|
||||
"start": 0,
|
||||
"count": 1,
|
||||
"sessionid": this.getSessionID(),
|
||||
"feature2": -1
|
||||
url: `https://steamcommunity.com/comment/Profile/delete/${userID.toString()}/-1`,
|
||||
form: {
|
||||
gidcomment: commentID,
|
||||
start: 0,
|
||||
count: 1,
|
||||
sessionid: this.getSessionID(),
|
||||
feature2: -1
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,20 +182,20 @@ SteamCommunity.prototype.deleteUserComment = function(userID, commentID, callbac
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.success && !body.comments_html.includes(commentID)) {
|
||||
if (body.success && !body.comments_html.includes(commentID)) {
|
||||
callback(null);
|
||||
} else if(body.error) {
|
||||
} else if (body.error) {
|
||||
callback(new Error(body.error));
|
||||
} else if(body.comments_html.includes(commentID)) {
|
||||
callback(new Error("Failed to delete comment"));
|
||||
} else if (body.comments_html.includes(commentID)) {
|
||||
callback(new Error('Failed to delete comment'));
|
||||
} else {
|
||||
callback(new Error("Unknown error"));
|
||||
callback(new Error('Unknown error'));
|
||||
}
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getUserComments = function(userID, options, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
@@ -211,19 +204,19 @@ SteamCommunity.prototype.getUserComments = function(userID, options, callback) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
var form = Object.assign({
|
||||
"start": 0,
|
||||
"count": 0,
|
||||
"feature2": -1,
|
||||
"sessionid": this.getSessionID()
|
||||
let form = Object.assign({
|
||||
start: 0,
|
||||
count: 0,
|
||||
feature2: -1,
|
||||
sessionid: this.getSessionID()
|
||||
}, options);
|
||||
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/comment/Profile/render/" + userID.toString() + "/-1",
|
||||
"form": form,
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
url: `https://steamcommunity.com/comment/Profile/render/${userID.toString()}/-1`,
|
||||
form,
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -232,52 +225,51 @@ SteamCommunity.prototype.getUserComments = function(userID, options, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.success) {
|
||||
if (body.success) {
|
||||
const $ = Cheerio.load(body.comments_html);
|
||||
const comments = $(".commentthread_comment.responsive_body_text[id]").map((i, elem) => {
|
||||
var $elem = $(elem),
|
||||
$commentContent = $elem.find(".commentthread_comment_text");
|
||||
const comments = $('.commentthread_comment.responsive_body_text[id]').map((i, elem) => {
|
||||
let $elem = $(elem),
|
||||
$commentContent = $elem.find('.commentthread_comment_text');
|
||||
return {
|
||||
id: $elem.attr("id").split("_")[1],
|
||||
id: $elem.attr('id').split('_')[1],
|
||||
author: {
|
||||
steamID: new SteamID("[U:1:" + $elem.find("[data-miniprofile]").data("miniprofile") + "]"),
|
||||
name: $elem.find("bdi").text(),
|
||||
avatar: $elem.find(".playerAvatar img[src]").attr("src"),
|
||||
state: $elem.find(".playerAvatar").attr("class").split(" ").pop()
|
||||
steamID: new SteamID('[U:1:' + $elem.find('[data-miniprofile]').data('miniprofile') + ']'),
|
||||
name: $elem.find('bdi').text(),
|
||||
avatar: $elem.find('.playerAvatar img[src]').attr('src'),
|
||||
state: $elem.find('.playerAvatar').attr('class').split(' ').pop()
|
||||
},
|
||||
date: new Date($elem.find(".commentthread_comment_timestamp").data("timestamp") * 1000),
|
||||
date: new Date($elem.find('.commentthread_comment_timestamp').data('timestamp') * 1000),
|
||||
text: $commentContent.text().trim(),
|
||||
html: $commentContent.html().trim()
|
||||
}
|
||||
};
|
||||
}).get();
|
||||
|
||||
callback(null, comments, body.total_count);
|
||||
} else if(body.error) {
|
||||
} else if (body.error) {
|
||||
callback(new Error(body.error));
|
||||
} else {
|
||||
callback(new Error("Unknown error"));
|
||||
callback(new Error('Unknown error'));
|
||||
}
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.inviteUserToGroup = function(userID, groupID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://steamcommunity.com/actions/GroupInvite",
|
||||
"form": {
|
||||
"group": groupID.toString(),
|
||||
"invitee": userID.toString(),
|
||||
"json": 1,
|
||||
"sessionID": this.getSessionID(),
|
||||
"type": "groupInvite"
|
||||
url: 'https://steamcommunity.com/actions/GroupInvite',
|
||||
form: {
|
||||
group: groupID.toString(),
|
||||
invitee: userID.toString(),
|
||||
json: 1,
|
||||
sessionID: this.getSessionID(),
|
||||
type: 'groupInvite'
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -286,74 +278,14 @@ SteamCommunity.prototype.inviteUserToGroup = function(userID, groupID, callback)
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.results == 'OK') {
|
||||
if (body.results == 'OK') {
|
||||
callback(null);
|
||||
} else if(body.results) {
|
||||
} else if (body.results) {
|
||||
callback(new Error(body.results));
|
||||
} else {
|
||||
callback(new Error("Unknown error"));
|
||||
callback(new Error('Unknown error'));
|
||||
}
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.followUser = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
"uri": `https://steamcommunity.com/profiles/${userID.toString()}/followuser/`,
|
||||
"form": {
|
||||
"sessionid": this.getSessionID(),
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success && body.success != SteamCommunity.EResult.OK) {
|
||||
callback(Helpers.eresultError(body.success));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.unfollowUser = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
"uri": `https://steamcommunity.com/profiles/${userID.toString()}/unfollowuser/`,
|
||||
"form": {
|
||||
"sessionid": this.getSessionID(),
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.success && body.success != SteamCommunity.EResult.OK) {
|
||||
callback(Helpers.eresultError(body.success));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getUserAliases = function(userID, callback) {
|
||||
@@ -362,24 +294,24 @@ SteamCommunity.prototype.getUserAliases = function(userID, callback) {
|
||||
}
|
||||
|
||||
this.httpRequestGet({
|
||||
"uri": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/ajaxaliases",
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
url: `https://steamcommunity.com/profiles/${userID.getSteamID64()}/ajaxaliases`,
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof body !== 'object') {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, body.map(function(entry) {
|
||||
callback(null, body.map((entry) => {
|
||||
entry.timechanged = Helpers.decodeSteamTime(entry.timechanged);
|
||||
return entry;
|
||||
}));
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -392,33 +324,33 @@ SteamCommunity.prototype.getUserProfileBackground = function(userID, callback) {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64(), (err, response, body) => {
|
||||
this.httpRequest(`https://steamcommunity.com/profiles/${userID.getSteamID64()}`, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body);
|
||||
let $ = Cheerio.load(body);
|
||||
|
||||
var $privateProfileInfo = $('.profile_private_info');
|
||||
let $privateProfileInfo = $('.profile_private_info');
|
||||
if ($privateProfileInfo.length > 0) {
|
||||
callback(new Error($privateProfileInfo.text().trim()));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($('body').hasClass('has_profile_background')) {
|
||||
var backgroundUrl = $('div.profile_background_image_content').css('background-image');
|
||||
var matcher = backgroundUrl.match(/\(([^)]+)\)/);
|
||||
let backgroundUrl = $('div.profile_background_image_content').css('background-image');
|
||||
let matcher = backgroundUrl.match(/\(([^)]+)\)/);
|
||||
|
||||
if (matcher.length != 2 || !matcher[1].length) {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
} else {
|
||||
callback(null, matcher[1]);
|
||||
}
|
||||
} else {
|
||||
callback(null, null);
|
||||
}
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
|
||||
@@ -432,28 +364,27 @@ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
|
||||
}
|
||||
|
||||
if (!userID) {
|
||||
callback(new Error("No SteamID specified and not logged in"));
|
||||
callback(new Error('No SteamID specified and not logged in'));
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory/", function(err, response, body) {
|
||||
this.httpRequest(`https://steamcommunity.com/profiles/${userID.getSteamID64()}/inventory/`, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/);
|
||||
let match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/);
|
||||
if (!match) {
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
var data;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(match[1]);
|
||||
} catch(e) {
|
||||
callback(new Error("Malformed response"));
|
||||
} catch (e) {
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -473,7 +404,7 @@ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
}, "steamcommunity");
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -486,27 +417,22 @@ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
|
||||
* @param {function} callback
|
||||
*/
|
||||
SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, tradableOnly, callback) {
|
||||
var self = this;
|
||||
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var endpoint = "/profiles/" + userID.getSteamID64();
|
||||
get([], []);
|
||||
|
||||
function get(inventory, currency, start) {
|
||||
self.httpRequest({
|
||||
"uri": "https://steamcommunity.com" + endpoint + "/inventory/json/" + appID + "/" + contextID,
|
||||
"headers": {
|
||||
"Referer": "https://steamcommunity.com" + endpoint + "/inventory"
|
||||
const get = (inventory, currency, start) => {
|
||||
this.httpRequest({
|
||||
url: `https://steamcommunity.com${endpoint}/inventory/json/${appID}/${contextID}`,
|
||||
headers: {
|
||||
Referer: `https://steamcommunity.com${endpoint}/inventory`
|
||||
},
|
||||
"qs": {
|
||||
"start": start,
|
||||
"trading": tradableOnly ? 1 : undefined
|
||||
qs: {
|
||||
start: start,
|
||||
trading: tradableOnly ? 1 : undefined
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
@@ -514,43 +440,37 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t
|
||||
|
||||
if (!body || !body.success || !body.rgInventory || !body.rgDescriptions || !body.rgCurrency) {
|
||||
if (body) {
|
||||
callback(new Error(body.Error || "Malformed response"));
|
||||
callback(new Error(body.Error || 'Malformed response'));
|
||||
} else {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var i;
|
||||
for (i in body.rgInventory) {
|
||||
if (!body.rgInventory.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let i in body.rgInventory) {
|
||||
inventory.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
|
||||
}
|
||||
|
||||
for (i in body.rgCurrency) {
|
||||
if (!body.rgCurrency.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let i in body.rgCurrency) {
|
||||
currency.push(new CEconItem(body.rgCurrency[i], body.rgDescriptions, contextID));
|
||||
}
|
||||
|
||||
if (body.more) {
|
||||
var match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//);
|
||||
if(match) {
|
||||
endpoint = "/" + match[1] + "/" + match[2];
|
||||
let match = response.request.uri.href.match(/\/(profiles|id)\/([^/]+)\//);
|
||||
if (match) {
|
||||
endpoint = `/${match[1]}/${match[2]}`;
|
||||
}
|
||||
|
||||
get(inventory, currency, body.more_start);
|
||||
} else {
|
||||
callback(null, inventory, currency);
|
||||
}
|
||||
}, "steamcommunity");
|
||||
}
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
let endpoint = `/profiles/${userID.getSteamID64()}`;
|
||||
get([], []);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -565,52 +485,64 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t
|
||||
SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) {
|
||||
if (typeof language === 'function') {
|
||||
callback = language;
|
||||
language = "english";
|
||||
language = 'english';
|
||||
}
|
||||
|
||||
if (!userID) {
|
||||
callback(new Error("The user's SteamID is invalid or missing."));
|
||||
callback(new Error('The user\'s SteamID is invalid or missing.'));
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var pos = 1;
|
||||
get([], []);
|
||||
// A bit of optimization; objects are hash tables so it's more efficient to look up by key than to iterate an array
|
||||
let quickDescriptionLookup = {};
|
||||
|
||||
function get(inventory, currency, start) {
|
||||
self.httpRequest({
|
||||
"uri": "https://steamcommunity.com/inventory/" + userID.getSteamID64() + "/" + appID + "/" + contextID,
|
||||
"headers": {
|
||||
"Referer": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory"
|
||||
const getDescription = (descriptions, classID, instanceID) => {
|
||||
let key = classID + '_' + (instanceID || '0'); // instanceID can be undefined, in which case it's 0.
|
||||
|
||||
if (quickDescriptionLookup[key]) {
|
||||
return quickDescriptionLookup[key];
|
||||
}
|
||||
|
||||
for (let i = 0; i < descriptions.length; i++) {
|
||||
quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i];
|
||||
}
|
||||
|
||||
return quickDescriptionLookup[key];
|
||||
};
|
||||
|
||||
const get = (inventory, currency, start) => {
|
||||
this.httpRequest({
|
||||
url: `https://steamcommunity.com/inventory/${userID.getSteamID64()}/${appID}/${contextID}`,
|
||||
headers: {
|
||||
Referer: `https://steamcommunity.com/profiles/${userID.getSteamID64()}/inventory`
|
||||
},
|
||||
"qs": {
|
||||
"l": language, // Default language
|
||||
"count": 1000, // Max items per 'page'
|
||||
"start_assetid": start
|
||||
qs: {
|
||||
l: language, // Default language
|
||||
count: 2000, // Max items per 'page'
|
||||
start_assetid: start
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
json: true
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
if (err.message == "HTTP error 403" && body === null) {
|
||||
if (err.message == 'HTTP error 403' && body === null) {
|
||||
// 403 with a body of "null" means the inventory/profile is private.
|
||||
if (self.steamID && userID.getSteamID64() == self.steamID.getSteamID64()) {
|
||||
if (this.steamID && userID.getSteamID64() == this.steamID.getSteamID64()) {
|
||||
// We can never get private profile error for our own inventory!
|
||||
self._notifySessionExpired(err);
|
||||
this._notifySessionExpired(err);
|
||||
}
|
||||
|
||||
callback(new Error("This profile is private."));
|
||||
callback(new Error('This profile is private.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.message == "HTTP error 500" && body && body.error) {
|
||||
if (err.message == 'HTTP error 500' && body && body.error) {
|
||||
err = new Error(body.error);
|
||||
|
||||
var match = body.error.match(/^(.+) \((\d+)\)$/);
|
||||
let match = body.error.match(/^(.+) \((\d+)\)$/);
|
||||
if (match) {
|
||||
err.message = match[1];
|
||||
err.eresult = match[2];
|
||||
@@ -629,26 +561,19 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
|
||||
return;
|
||||
}
|
||||
|
||||
if (appID == 730 && body && body.success && !body.assets) {
|
||||
// CS inventory has no visible items. We need a special case for this because Valve is incapable of
|
||||
// doing anything not dumb.
|
||||
callback(null, [], [], body.total_inventory_count);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!body || !body.success || !body.assets || !body.descriptions) {
|
||||
if (body) {
|
||||
// Dunno if the error/Error property even exists on this new endpoint
|
||||
callback(new Error(body.error || body.Error || "Malformed response"));
|
||||
callback(new Error(body.error || body.Error || 'Malformed response'));
|
||||
} else {
|
||||
callback(new Error("Malformed response"));
|
||||
callback(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < body.assets.length; i++) {
|
||||
var description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid);
|
||||
for (let i = 0; i < body.assets.length; i++) {
|
||||
let description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid);
|
||||
|
||||
if (!tradableOnly || (description && description.tradable)) {
|
||||
body.assets[i].pos = pos++;
|
||||
@@ -661,25 +586,11 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
|
||||
} else {
|
||||
callback(null, inventory, currency, body.total_inventory_count);
|
||||
}
|
||||
}, "steamcommunity");
|
||||
}
|
||||
}, 'steamcommunity');
|
||||
};
|
||||
|
||||
// A bit of optimization; objects are hash tables so it's more efficient to look up by key than to iterate an array
|
||||
var quickDescriptionLookup = {};
|
||||
|
||||
function getDescription(descriptions, classID, instanceID) {
|
||||
var key = classID + '_' + (instanceID || '0'); // instanceID can be undefined, in which case it's 0.
|
||||
|
||||
if (quickDescriptionLookup[key]) {
|
||||
return quickDescriptionLookup[key];
|
||||
}
|
||||
|
||||
for (var i = 0; i < descriptions.length; i++) {
|
||||
quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i];
|
||||
}
|
||||
|
||||
return quickDescriptionLookup[key];
|
||||
}
|
||||
let pos = 1;
|
||||
get([], []);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -711,7 +622,7 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer,
|
||||
return;
|
||||
}
|
||||
|
||||
var imageDetails = null;
|
||||
let imageDetails = null;
|
||||
try {
|
||||
imageDetails = imageSize(imageContentsBuffer);
|
||||
} catch (ex) {
|
||||
@@ -719,14 +630,14 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer,
|
||||
return;
|
||||
}
|
||||
|
||||
var imageHash = Crypto.createHash('sha1');
|
||||
let imageHash = Crypto.createHash('sha1');
|
||||
imageHash.update(imageContentsBuffer);
|
||||
imageHash = imageHash.digest('hex');
|
||||
|
||||
var filename = Date.now() + '_image.' + imageDetails.type;
|
||||
let filename = Date.now() + '_image.' + imageDetails.type;
|
||||
|
||||
this.httpRequestPost({
|
||||
uri: 'https://steamcommunity.com/chat/beginfileupload/?l=english',
|
||||
url: 'https://steamcommunity.com/chat/beginfileupload/?l=english',
|
||||
headers: {
|
||||
referer: 'https://steamcommunity.com/chat/'
|
||||
},
|
||||
@@ -744,7 +655,7 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer,
|
||||
}, (err, res, body) => {
|
||||
if (err) {
|
||||
if (body && body.success) {
|
||||
var err2 = Helpers.eresultError(body.success);
|
||||
let err2 = Helpers.eresultError(body.success);
|
||||
if (body.message) {
|
||||
err2.message = body.message;
|
||||
}
|
||||
@@ -760,9 +671,9 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer,
|
||||
return;
|
||||
}
|
||||
|
||||
var hmac = body.hmac;
|
||||
var timestamp = body.timestamp;
|
||||
var startResult = body.result;
|
||||
let hmac = body.hmac;
|
||||
let timestamp = body.timestamp;
|
||||
let startResult = body.result;
|
||||
|
||||
if (!startResult || !startResult.ugcid || !startResult.url_host || !startResult.request_headers) {
|
||||
callback(new Error('Malformed response'));
|
||||
@@ -770,14 +681,14 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer,
|
||||
}
|
||||
|
||||
// Okay, now we need to PUT the file to the provided URL
|
||||
var uploadUrl = (startResult.use_https ? 'https' : 'http') + '://' + startResult.url_host + startResult.url_path;
|
||||
var headers = {};
|
||||
let uploadUrl = (startResult.use_https ? 'https' : 'http') + '://' + startResult.url_host + startResult.url_path;
|
||||
let headers = {};
|
||||
startResult.request_headers.forEach((header) => {
|
||||
headers[header.name.toLowerCase()] = header.value;
|
||||
});
|
||||
|
||||
this.httpRequest({
|
||||
uri: uploadUrl,
|
||||
url: uploadUrl,
|
||||
method: 'PUT',
|
||||
headers,
|
||||
body: imageContentsBuffer
|
||||
@@ -789,7 +700,7 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer,
|
||||
|
||||
// Now we need to commit the upload
|
||||
this.httpRequestPost({
|
||||
uri: 'https://steamcommunity.com/chat/commitfileupload/',
|
||||
url: 'https://steamcommunity.com/chat/commitfileupload/',
|
||||
headers: {
|
||||
referer: 'https://steamcommunity.com/chat/'
|
||||
},
|
||||
|
||||
@@ -1,166 +1,54 @@
|
||||
const StdLib = require('@doctormckay/stdlib');
|
||||
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
const Helpers = require('./helpers.js');
|
||||
|
||||
/**
|
||||
* Retrieves your account's Steam Web API key, if you already have one. If you don't yet have one, this will fail.
|
||||
* To create a Web API key, use `createWebApiKey()`.
|
||||
*
|
||||
* @param {null|function} unused - No longer used, kept for backward compatibility. You can omit this parameter and pass
|
||||
* your callback directly as the first parameter if you want.
|
||||
* @param {function} callback
|
||||
* @param {string} domain
|
||||
* @param {function} [callback]
|
||||
* @return Promise<{key: string}>
|
||||
*/
|
||||
SteamCommunity.prototype.getWebApiKey = function(unused, callback) {
|
||||
if (typeof unused == 'function') {
|
||||
callback = unused;
|
||||
}
|
||||
SteamCommunity.prototype.getWebApiKey = function(domain, callback) {
|
||||
return StdLib.Promises.callbackPromise(['key'], callback, false, async (resolve, reject) => {
|
||||
let {textBody} = await this.httpRequest({
|
||||
method: 'GET',
|
||||
url: 'https://steamcommunity.com/dev/apikey?l=english',
|
||||
followRedirect: false,
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
this.httpRequest({
|
||||
uri: 'https://steamcommunity.com/dev/apikey?l=english',
|
||||
followRedirect: false
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
if (textBody.includes('<h2>Access Denied</h2>')) {
|
||||
return reject(new Error('Access Denied'));
|
||||
}
|
||||
|
||||
if (body.match(/You must have a validated email address to create a Steam Web API key./)) {
|
||||
return callback(new Error('You must have a validated email address to create a Steam Web API key.'));
|
||||
if (textBody.includes('You must have a validated email address to create a Steam Web API key.')) {
|
||||
return reject(new Error('You must have a validated email address to create a Steam Web API key.'));
|
||||
}
|
||||
|
||||
if (body.match(/Your account requires (<a [^>]+>)?Steam Guard Mobile Authenticator/)) {
|
||||
return callback(new Error('Steam Guard Mobile Authenticator required to create a Steam Web API key'));
|
||||
}
|
||||
|
||||
if (body.match(/<h2>Access Denied<\/h2>/)) {
|
||||
return callback(new Error('Access Denied'));
|
||||
}
|
||||
|
||||
let match = body.match(/<p>Key: ([0-9A-F]+)<\/p>/);
|
||||
let match = textBody.match(/<p>Key: ([0-9A-F]+)<\/p>/);
|
||||
if (match) {
|
||||
// We already have an API key registered
|
||||
callback(null, match[1]);
|
||||
} else {
|
||||
callback(new Error('No API key created for this account'));
|
||||
}
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef CreateApiKeyOptions
|
||||
* @property {string} domain - The domain to associate with your API key
|
||||
* @property {string} [requestID] - If finalizing an existing create request, include the request ID
|
||||
* @property {string|Buffer} [identitySecret] - If you pass your identity_secret here, then steamcommunity will
|
||||
* internally handle accepting any confirmations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef CreateApiKeyResponse
|
||||
* @property {boolean} confirmationRequired
|
||||
* @property {string} [apiKey] - If creating your API key succeeded, this is the new key
|
||||
* @property {CreateApiKeyOptions} [finalizeOptions] - If confirmation is required to create a key, then accept the
|
||||
* confirmation, then call createWebApiKey again and pass this whole object for the `options` parameter.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback createWebApiKeyCallback
|
||||
* @param {Error|null} err
|
||||
* @param {CreateApiKeyResponse} [result]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Starts the process to create a Steam Web API key. When the callback is fired, you will need to approve a mobile
|
||||
* confirmation in your app or using getConfirmations().
|
||||
*
|
||||
* @param {CreateApiKeyOptions} options
|
||||
* @param {createWebApiKeyCallback} callback
|
||||
*/
|
||||
SteamCommunity.prototype.createWebApiKey = function(options, callback) {
|
||||
if (!options.domain) {
|
||||
callback(new Error('Passing a domain is required to register an API key'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.httpRequestPost({
|
||||
uri: 'https://steamcommunity.com/dev/requestkey',
|
||||
form: {
|
||||
domain: options.domain,
|
||||
request_id: options.requestID || '0',
|
||||
sessionid: this.getSessionID(),
|
||||
agreeToTerms: 'true'
|
||||
},
|
||||
json: true
|
||||
}, (err, res, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
return resolve({key: match[1]});
|
||||
}
|
||||
|
||||
// body.requires_confirmation is 1/0, but the Steam website doesn't check this value and instead only checks the
|
||||
// value of `success`. So let's just do that.
|
||||
// We need to register a new API key
|
||||
await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: 'https://steamcommunity.com/dev/registerkey?l=english',
|
||||
form: {
|
||||
domain,
|
||||
agreeToTerms: 'agreed',
|
||||
sessionid: this.getSessionID(),
|
||||
Submit: 'Register'
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
// This is a mess. I'm glad we have promises and await now.
|
||||
|
||||
switch (body.success) {
|
||||
case SteamCommunity.EResult.OK:
|
||||
if (body.api_key) {
|
||||
callback(null, {confirmationRequired: false, apiKey: body.api_key});
|
||||
return;
|
||||
}
|
||||
|
||||
// It's not been observed that we get result OK without api_key included, but the Steam website doesn't
|
||||
// use this value so let's be safe just in case it disappears in the future.
|
||||
this.getWebApiKey((err, key) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {confirmationRequired: false, apiKey: key});
|
||||
});
|
||||
return;
|
||||
|
||||
case SteamCommunity.EResult.Pending:
|
||||
let finalizeOptions = {
|
||||
domain: options.domain,
|
||||
requestID: body.request_id || options.requestID
|
||||
}
|
||||
|
||||
if (options.identitySecret) {
|
||||
this.acceptConfirmationForObject(options.identitySecret, finalizeOptions.requestID, (err) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
this.createWebApiKey(finalizeOptions, callback);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
confirmationRequired: true,
|
||||
finalizeOptions: finalizeOptions
|
||||
});
|
||||
return;
|
||||
|
||||
default:
|
||||
callback(Helpers.eresultError(body.success));
|
||||
}
|
||||
resolve({key: await this.getWebApiKey(domain)});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated No longer works. Will be removed in a future release.
|
||||
* @param {function} callback
|
||||
*/
|
||||
SteamCommunity.prototype.getWebApiOauthToken = function(callback) {
|
||||
if (this.oAuthToken) {
|
||||
return callback(null, this.oAuthToken);
|
||||
}
|
||||
|
||||
callback(new Error('This operation requires an OAuth token, which is no longer issued by Steam.'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets an access_token generated by steam-session using EAuthTokenPlatformType.MobileApp.
|
||||
* Required for some operations such as 2FA enabling and disabling.
|
||||
|
||||
1
examples/.gitignore
vendored
1
examples/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
twofactor_*.json
|
||||
@@ -1,7 +1,7 @@
|
||||
// If you aren't running this script inside of the repository, replace the following line with:
|
||||
// const SteamCommunity = require('steamcommunity');
|
||||
const SteamCommunity = require('../index.js');
|
||||
const SteamTotp = require('steam-totp');
|
||||
const SteamSession = require('steam-session');
|
||||
const ReadLine = require('readline');
|
||||
|
||||
let g_AbortPromptFunc = null;
|
||||
@@ -13,32 +13,69 @@ async function main() {
|
||||
let accountName = await promptAsync('Username: ');
|
||||
let password = await promptAsync('Password (hidden): ', true);
|
||||
|
||||
attemptLogin(accountName, password);
|
||||
}
|
||||
// Create a LoginSession for us to use to attempt to log into steam
|
||||
let session = new SteamSession.LoginSession(SteamSession.EAuthTokenPlatformType.MobileApp);
|
||||
|
||||
function attemptLogin(accountName, password, twoFactorCode) {
|
||||
community.login({
|
||||
accountName,
|
||||
password,
|
||||
twoFactorCode,
|
||||
disableMobile: false
|
||||
}, async (err) => {
|
||||
if (err && err.message == 'SteamGuardMobile') {
|
||||
let code = await promptAsync('Steam Guard App Code OR Shared Secret: ');
|
||||
if (code.length > 5) {
|
||||
// If we were provided a shared secret, turn it into a code.
|
||||
code = SteamTotp.getAuthCode(code);
|
||||
}
|
||||
attemptLogin(accountName, password, code);
|
||||
return;
|
||||
}
|
||||
// Go ahead and attach our event handlers before we do anything else.
|
||||
session.on('authenticated', async () => {
|
||||
abortPrompt();
|
||||
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
let accessToken = session.accessToken;
|
||||
let cookies = await session.getWebCookies();
|
||||
|
||||
community.setCookies(cookies);
|
||||
community.setMobileAppAccessToken(accessToken);
|
||||
|
||||
// Enabling or disabling 2FA is presently the only action in node-steamcommunity which requires an access token.
|
||||
// In all other cases, using `community.setCookies(cookies)` is all you need to do in order to be logged in,
|
||||
// although there's never any harm in setting a mobile app access token.
|
||||
|
||||
doRevoke();
|
||||
});
|
||||
|
||||
session.on('timeout', () => {
|
||||
abortPrompt();
|
||||
console.log('This login attempt has timed out.');
|
||||
});
|
||||
|
||||
session.on('error', (err) => {
|
||||
abortPrompt();
|
||||
|
||||
// This should ordinarily not happen. This only happens in case there's some kind of unexpected error while
|
||||
// polling, e.g. the network connection goes down or Steam chokes on something.
|
||||
|
||||
console.log(`ERROR: This login attempt has failed! ${err.message}`);
|
||||
});
|
||||
|
||||
// Start our login attempt
|
||||
let startResult = await session.startWithCredentials({accountName, password});
|
||||
if (startResult.actionRequired) {
|
||||
// Some Steam Guard action is required. We only care about email and device codes; in theory an
|
||||
// EmailConfirmation and/or DeviceConfirmation action could be possible, but we're just going to ignore those.
|
||||
// If the user does receive a confirmation and accepts it, LoginSession will detect and handle that automatically.
|
||||
// The only consequence of ignoring it here is that we don't print a message to the user indicating that they
|
||||
// could accept an email or device confirmation.
|
||||
|
||||
let codeActionTypes = [SteamSession.EAuthSessionGuardType.EmailCode, SteamSession.EAuthSessionGuardType.DeviceCode];
|
||||
let codeAction = startResult.validActions.find(action => codeActionTypes.includes(action.type));
|
||||
if (codeAction) {
|
||||
if (codeAction.type == SteamSession.EAuthSessionGuardType.EmailCode) {
|
||||
// We wouldn't expect this to happen since we're trying to disable 2FA, but just in case...
|
||||
console.log(`A code has been sent to your email address at ${codeAction.detail}.`);
|
||||
} else {
|
||||
console.log('You need to provide a Steam Guard Mobile Authenticator code.');
|
||||
}
|
||||
|
||||
let code = await promptAsync('Code: ');
|
||||
if (code) {
|
||||
await session.submitSteamGuardCode(code);
|
||||
}
|
||||
|
||||
// If we fall through here without submitting a Steam Guard code, that means one of two things:
|
||||
// 1. The user pressed enter without providing a code, in which case the script will simply exit
|
||||
// 2. The user approved a device/email confirmation, in which case 'authenticated' was emitted and the prompt was canceled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function doRevoke() {
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var ReadLine = require('readline');
|
||||
let SteamCommunity = require('../index.js');
|
||||
let ReadLine = require('readline');
|
||||
|
||||
var community = new SteamCommunity();
|
||||
var rl = ReadLine.createInterface({
|
||||
"input": process.stdin,
|
||||
"output": process.stdout
|
||||
let community = new SteamCommunity();
|
||||
let rl = ReadLine.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.question("Username: ", function(accountName) {
|
||||
rl.question("Password: ", function(password) {
|
||||
rl.question('Username: ', function(accountName) {
|
||||
rl.question('Password: ', function(password) {
|
||||
doLogin(accountName, password);
|
||||
});
|
||||
});
|
||||
|
||||
function doLogin(accountName, password, authCode, twoFactorCode, captcha) {
|
||||
community.login({
|
||||
"accountName": accountName,
|
||||
"password": password,
|
||||
"authCode": authCode,
|
||||
"twoFactorCode": twoFactorCode,
|
||||
"captcha": captcha
|
||||
accountName: accountName,
|
||||
password: password,
|
||||
authCode: authCode,
|
||||
twoFactorCode: twoFactorCode,
|
||||
captcha: captcha
|
||||
}, function(err, sessionID, cookies, steamguard) {
|
||||
if(err) {
|
||||
if(err.message == 'SteamGuardMobile') {
|
||||
rl.question("Steam Authenticator Code: ", function(code) {
|
||||
if (err) {
|
||||
if (err.message == 'SteamGuardMobile') {
|
||||
rl.question('Steam Authenticator Code: ', function(code) {
|
||||
doLogin(accountName, password, null, code);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(err.message == 'SteamGuard') {
|
||||
console.log("An email has been sent to your address at " + err.emaildomain);
|
||||
rl.question("Steam Guard Code: ", function(code) {
|
||||
if (err.message == 'SteamGuard') {
|
||||
console.log('An email has been sent to your address at ' + err.emaildomain);
|
||||
rl.question('Steam Guard Code: ', function(code) {
|
||||
doLogin(accountName, password, code);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(err.message == 'CAPTCHA') {
|
||||
if (err.message == 'CAPTCHA') {
|
||||
console.log(err.captchaurl);
|
||||
rl.question("CAPTCHA: ", function(captchaInput) {
|
||||
rl.question('CAPTCHA: ', function(captchaInput) {
|
||||
doLogin(accountName, password, authCode, twoFactorCode, captchaInput);
|
||||
});
|
||||
|
||||
@@ -53,9 +53,9 @@ function doLogin(accountName, password, authCode, twoFactorCode, captcha) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Logged on!");
|
||||
console.log('Logged on!');
|
||||
|
||||
rl.question("Group ID: ", function(gid) {
|
||||
rl.question('Group ID: ', function(gid) {
|
||||
community.getSteamGroup(gid, function(err, group) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
@@ -64,19 +64,19 @@ function doLogin(accountName, password, authCode, twoFactorCode, captcha) {
|
||||
|
||||
group.getAllAnnouncements(function(err, announcements) {
|
||||
|
||||
if(announcements.length === 0) {
|
||||
return console.log("This group has no announcements");
|
||||
if (announcements.length === 0) {
|
||||
return console.log('This group has no announcements');
|
||||
}
|
||||
|
||||
for (var i = announcements.length - 1; i >= 0; i--) {
|
||||
console.log("[%s] %s %s: %s", announcements[i].date, announcements[i].aid, announcements[i].author, announcements[i].content);
|
||||
};
|
||||
for (let i = announcements.length - 1; i >= 0; i--) {
|
||||
console.log('[%s] %s %s: %s', announcements[i].date, announcements[i].aid, announcements[i].author, announcements[i].content);
|
||||
}
|
||||
|
||||
rl.question("Would you like to delete delete or edit an annoucement? (Type edit/delete): ", function(choice) {
|
||||
rl.question("Annoucement ID: ", function(aid) {
|
||||
if(choice === 'edit') {
|
||||
rl.question("New title: ", function(header) {
|
||||
rl.question("New body: ", function(content) {
|
||||
rl.question('Would you like to delete delete or edit an annoucement? (Type edit/delete): ', function(choice) {
|
||||
rl.question('Annoucement ID: ', function(aid) {
|
||||
if (choice === 'edit') {
|
||||
rl.question('New title: ', function(header) {
|
||||
rl.question('New body: ', function(content) {
|
||||
// EW THE PYRAMID!
|
||||
// Try replace this with delete!
|
||||
editAnnouncement(group, aid, header, content);
|
||||
@@ -96,10 +96,10 @@ function doLogin(accountName, password, authCode, twoFactorCode, captcha) {
|
||||
function editAnnouncement(group, aid, header, content) {
|
||||
// Actual community method.
|
||||
group.editAnnouncement(aid, header, content, function(error) {
|
||||
if(!error) {
|
||||
console.log("Annoucement edited!");
|
||||
if (!error) {
|
||||
console.log('Annoucement edited!');
|
||||
} else {
|
||||
console.log("Unable to edit annoucement! %j", error);
|
||||
console.log('Unable to edit annoucement! %j', error);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
@@ -109,10 +109,10 @@ function deleteAnnouncement(group, aid) {
|
||||
// group.deleteAnnouncement(aid);
|
||||
// Or
|
||||
group.deleteAnnouncement(aid, function(err) {
|
||||
if(!err) {
|
||||
console.log("Deleted");
|
||||
if (!err) {
|
||||
console.log('Deleted');
|
||||
} else {
|
||||
console.log("Error deleting announcement.");
|
||||
console.log('Error deleting announcement.');
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// If you aren't running this script inside of the repository, replace the following line with:
|
||||
// const SteamCommunity = require('steamcommunity');
|
||||
const SteamCommunity = require('../index.js');
|
||||
const SteamSession = require('steam-session');
|
||||
const ReadLine = require('readline');
|
||||
const FS = require('fs');
|
||||
|
||||
@@ -15,28 +16,69 @@ async function main() {
|
||||
let accountName = await promptAsync('Username: ');
|
||||
let password = await promptAsync('Password (hidden): ', true);
|
||||
|
||||
attemptLogin(accountName, password);
|
||||
}
|
||||
// Create a LoginSession for us to use to attempt to log into steam
|
||||
let session = new SteamSession.LoginSession(SteamSession.EAuthTokenPlatformType.MobileApp);
|
||||
|
||||
function attemptLogin(accountName, password, authCode) {
|
||||
community.login({
|
||||
accountName,
|
||||
password,
|
||||
authCode,
|
||||
disableMobile: false
|
||||
}, async (err) => {
|
||||
if (err && err.message == 'SteamGuard') {
|
||||
let code = await promptAsync('Steam Guard Email Code: ');
|
||||
attemptLogin(accountName, password, code);
|
||||
return;
|
||||
}
|
||||
// Go ahead and attach our event handlers before we do anything else.
|
||||
session.on('authenticated', async () => {
|
||||
abortPrompt();
|
||||
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
let accessToken = session.accessToken;
|
||||
let cookies = await session.getWebCookies();
|
||||
|
||||
community.setCookies(cookies);
|
||||
community.setMobileAppAccessToken(accessToken);
|
||||
|
||||
// Enabling or disabling 2FA is presently the only action in node-steamcommunity which requires an access token.
|
||||
// In all other cases, using `community.setCookies(cookies)` is all you need to do in order to be logged in,
|
||||
// although there's never any harm in setting a mobile app access token.
|
||||
|
||||
doSetup();
|
||||
});
|
||||
|
||||
session.on('timeout', () => {
|
||||
abortPrompt();
|
||||
console.log('This login attempt has timed out.');
|
||||
});
|
||||
|
||||
session.on('error', (err) => {
|
||||
abortPrompt();
|
||||
|
||||
// This should ordinarily not happen. This only happens in case there's some kind of unexpected error while
|
||||
// polling, e.g. the network connection goes down or Steam chokes on something.
|
||||
|
||||
console.log(`ERROR: This login attempt has failed! ${err.message}`);
|
||||
});
|
||||
|
||||
// Start our login attempt
|
||||
let startResult = await session.startWithCredentials({accountName, password});
|
||||
if (startResult.actionRequired) {
|
||||
// Some Steam Guard action is required. We only care about email and device codes; in theory an
|
||||
// EmailConfirmation and/or DeviceConfirmation action could be possible, but we're just going to ignore those.
|
||||
// If the user does receive a confirmation and accepts it, LoginSession will detect and handle that automatically.
|
||||
// The only consequence of ignoring it here is that we don't print a message to the user indicating that they
|
||||
// could accept an email or device confirmation.
|
||||
|
||||
let codeActionTypes = [SteamSession.EAuthSessionGuardType.EmailCode, SteamSession.EAuthSessionGuardType.DeviceCode];
|
||||
let codeAction = startResult.validActions.find(action => codeActionTypes.includes(action.type));
|
||||
if (codeAction) {
|
||||
if (codeAction.type == SteamSession.EAuthSessionGuardType.EmailCode) {
|
||||
console.log(`A code has been sent to your email address at ${codeAction.detail}.`);
|
||||
} else {
|
||||
// We wouldn't expect this to happen since we're trying to enable 2FA, but just in case...
|
||||
console.log('You need to provide a Steam Guard Mobile Authenticator code.');
|
||||
}
|
||||
|
||||
let code = await promptAsync('Code: ');
|
||||
if (code) {
|
||||
await session.submitSteamGuardCode(code);
|
||||
}
|
||||
|
||||
// If we fall through here without submitting a Steam Guard code, that means one of two things:
|
||||
// 1. The user pressed enter without providing a code, in which case the script will simply exit
|
||||
// 2. The user approved a device/email confirmation, in which case 'authenticated' was emitted and the prompt was canceled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doSetup() {
|
||||
@@ -76,13 +118,10 @@ function doSetup() {
|
||||
|
||||
async function promptActivationCode(response) {
|
||||
if (response.phone_number_hint) {
|
||||
console.log(`An activation code has been sent to your phone ending in ${response.phone_number_hint}.`);
|
||||
} else if (response.confirm_type == 3) {
|
||||
// Exact meaning of confirm_type is unknown, but 3 appears to be email code
|
||||
console.log('An activation code has been sent to your email.');
|
||||
console.log(`A code has been sent to your phone ending in ${response.phone_number_hint}.`);
|
||||
}
|
||||
|
||||
let smsCode = await promptAsync('Activation Code: ');
|
||||
let smsCode = await promptAsync('SMS Code: ');
|
||||
community.finalizeTwoFactor(response.shared_secret, smsCode, (err) => {
|
||||
if (err) {
|
||||
if (err.message == 'Invalid activation code') {
|
||||
|
||||
659
index.js
659
index.js
@@ -1,176 +1,214 @@
|
||||
const {chrome} = require('@doctormckay/user-agents');
|
||||
const Request = require('request');
|
||||
const {EventEmitter} = require('events');
|
||||
const StdLib = require('@doctormckay/stdlib');
|
||||
const SteamID = require('steamid');
|
||||
const {LoginSession, EAuthTokenPlatformType, EAuthSessionGuardType} = require('steam-session');
|
||||
const Util = require('util');
|
||||
const xml2js = require('xml2js');
|
||||
|
||||
const Helpers = require('./components/helpers.js');
|
||||
const Package = require('./package.json');
|
||||
|
||||
require('util').inherits(SteamCommunity, require('events').EventEmitter);
|
||||
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36';
|
||||
|
||||
Util.inherits(SteamCommunity, EventEmitter);
|
||||
|
||||
module.exports = SteamCommunity;
|
||||
|
||||
SteamCommunity.SteamID = SteamID;
|
||||
SteamCommunity.ConfirmationType = require('./resources/EConfirmationType.js');
|
||||
SteamCommunity.EConfirmationType = require('./resources/EConfirmationType.js');
|
||||
SteamCommunity.EResult = require('./resources/EResult.js');
|
||||
SteamCommunity.ESharedFileType = require('./resources/ESharedFileType.js');
|
||||
SteamCommunity.EFriendRelationship = require('./resources/EFriendRelationship.js');
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} [options]
|
||||
* @param {number} [options.timeout=50000] - The time in milliseconds that SteamCommunity will wait for HTTP requests to complete.
|
||||
* @param {string} [options.localAddress] - The local IP address that SteamCommunity will use for its HTTP requests.
|
||||
* @param {string} [options.httpProxy] - A string containing the URI of an HTTP proxy to use for all requests, e.g. `http://user:pass@1.2.3.4:8888`
|
||||
* @param {object} [options.defaultHttpHeaders] - An object containing some headers to send for every HTTP request
|
||||
* @constructor
|
||||
*/
|
||||
function SteamCommunity(options) {
|
||||
options = options || {};
|
||||
|
||||
this._jar = Request.jar();
|
||||
this.packageName = Package.name;
|
||||
this.packageVersion = Package.version;
|
||||
|
||||
this._jar = new StdLib.HTTP.CookieJar();
|
||||
this._captchaGid = -1;
|
||||
this._httpRequestID = 0;
|
||||
this.chatState = SteamCommunity.ChatState.Offline;
|
||||
|
||||
var defaults = {
|
||||
"jar": this._jar,
|
||||
"timeout": options.timeout || 50000,
|
||||
"gzip": true,
|
||||
"headers": {
|
||||
"User-Agent": options.userAgent || chrome()
|
||||
}
|
||||
let defaultHeaders = {
|
||||
'user-agent': USER_AGENT
|
||||
};
|
||||
|
||||
if (typeof options == "string") {
|
||||
options = {
|
||||
localAddress: options
|
||||
};
|
||||
// Apply the user's custom default headers
|
||||
for (let i in (options.defaultHttpHeaders || {})) {
|
||||
// Make sure all header names are lower case to avoid conflicts
|
||||
defaultHeaders[i.toLowerCase()] = options.defaultHttpHeaders[i];
|
||||
}
|
||||
|
||||
this._httpClient = new StdLib.HTTP.HttpClient({
|
||||
httpAgent: options.httpProxy ? StdLib.HTTP.getProxyAgent(false, options.httpProxy) : null,
|
||||
httpsAgent: options.httpProxy ? StdLib.HTTP.getProxyAgent(true, options.httpProxy) : null,
|
||||
localAddress: options.localAddress,
|
||||
defaultHeaders,
|
||||
defaultTimeout: options.timeout || 50000,
|
||||
cookieJar: this._jar,
|
||||
gzip: true
|
||||
});
|
||||
|
||||
this._options = options;
|
||||
|
||||
if (options.localAddress) {
|
||||
defaults.localAddress = options.localAddress;
|
||||
}
|
||||
|
||||
this.request = options.request || Request.defaults({"forever": true}); // "forever" indicates that we want a keep-alive agent
|
||||
this.request = this.request.defaults(defaults);
|
||||
|
||||
// English
|
||||
this._setCookie(Request.cookie('Steam_Language=english'));
|
||||
this._setCookie('Steam_Language=english');
|
||||
|
||||
// UTC
|
||||
this._setCookie(Request.cookie('timezoneOffset=0,0'));
|
||||
this._setCookie('timezoneOffset=0,0');
|
||||
}
|
||||
|
||||
SteamCommunity.prototype.login = function(details, callback) {
|
||||
if (!details.accountName || !details.password) {
|
||||
throw new Error("Missing either accountName or password to login; both are needed");
|
||||
/**
|
||||
* @param {object} details
|
||||
* @param {string} details.accountName
|
||||
* @param {string} details.password
|
||||
* @param {string} [details.authCode]
|
||||
* @param {string} [details.twoFactorCode]
|
||||
* @param {string} [details.authTokenPlatformType] - A value from steam-session's EAuthTokenPlatformType enum. Defaults to MobileApp.
|
||||
* @return Promise<{cookies: string[], sessionID: string, refreshToken: string}>
|
||||
*/
|
||||
SteamCommunity.prototype.login = function(details) {
|
||||
if (typeof details.accountName != 'string' || typeof details.password != 'string') {
|
||||
throw new Error('You must provide your accountName and password to login to steamcommunity.com');
|
||||
}
|
||||
|
||||
// Delete the cache
|
||||
delete this._profileURL;
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let platformType = details.authTokenPlatformType || EAuthTokenPlatformType.MobileApp;
|
||||
let session = new LoginSession(platformType, {
|
||||
httpProxy: this._options.httpProxy
|
||||
});
|
||||
|
||||
// default disableMobile to true
|
||||
let logOnOptions = Object.assign({}, details);
|
||||
logOnOptions.disableMobile = details.disableMobile !== false;
|
||||
session.on('authenticated', async () => {
|
||||
try {
|
||||
let cookies = await session.getWebCookies();
|
||||
this.setCookies(cookies);
|
||||
|
||||
this._modernLogin(logOnOptions).then(({sessionID, cookies, steamguard, mobileAccessToken}) => {
|
||||
this.setCookies(cookies);
|
||||
if (platformType == EAuthTokenPlatformType.MobileApp) {
|
||||
this.setMobileAppAccessToken(session.accessToken);
|
||||
}
|
||||
|
||||
if (mobileAccessToken) {
|
||||
this.setMobileAppAccessToken(mobileAccessToken);
|
||||
// TODO set refresh token for session keep-alive
|
||||
|
||||
let sessionID = this.getSessionID();
|
||||
if (!cookies.some(c => c.startsWith('sessionid='))) {
|
||||
// make sure that the sessionid we return is in the cookies list we return
|
||||
cookies.push(`sessionid=${sessionID}`);
|
||||
}
|
||||
|
||||
resolve({
|
||||
cookies,
|
||||
sessionID,
|
||||
refreshToken: session.refreshToken
|
||||
});
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
|
||||
session.on('timeout', () => {
|
||||
// This really shouldn't happen
|
||||
reject(new Error('Login attempt timed out'));
|
||||
});
|
||||
session.on('error', reject);
|
||||
|
||||
try {
|
||||
let startResult = await session.startWithCredentials({
|
||||
accountName: details.accountName,
|
||||
password: details.password,
|
||||
steamGuardCode: details.twoFactorCode || details.authCode
|
||||
});
|
||||
|
||||
if (!startResult.actionRequired) {
|
||||
return; // 'authenticated' should get emitted soon
|
||||
}
|
||||
|
||||
session.cancelLoginAttempt();
|
||||
|
||||
if (startResult.validActions.some(a => a.type == EAuthSessionGuardType.EmailCode)) {
|
||||
return reject(new Error('SteamGuard'));
|
||||
}
|
||||
|
||||
if (startResult.validActions.some(a => a.type == EAuthSessionGuardType.DeviceCode)) {
|
||||
return reject(new Error('SteamGuardMobile'));
|
||||
}
|
||||
|
||||
let validActions = startResult.validActions.map(a => a.type).join(', ');
|
||||
return reject(new Error(`Unexpected guard action(s) ${validActions}`));
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
|
||||
callback(null, sessionID, cookies, steamguard, null);
|
||||
}).catch(err => callback(err));
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param {string} steamguard
|
||||
* @param {string} token
|
||||
* @param {function} callback
|
||||
*/
|
||||
SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) {
|
||||
steamguard = steamguard.split('||');
|
||||
var steamID = new SteamID(steamguard[0]);
|
||||
|
||||
var self = this;
|
||||
this.httpRequestPost({
|
||||
"uri": "https://api.steampowered.com/IMobileAuthService/GetWGToken/v1/",
|
||||
"form": {
|
||||
"access_token": token
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body.response || !body.response.token || !body.response.token_secure) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
var cookies = [
|
||||
'steamLogin=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token),
|
||||
'steamLoginSecure=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token_secure),
|
||||
'steamMachineAuth' + steamID.getSteamID64() + '=' + steamguard[1],
|
||||
'sessionid=' + self.getSessionID()
|
||||
];
|
||||
|
||||
self.setCookies(cookies);
|
||||
callback(null, self.getSessionID(), cookies);
|
||||
}, "steamcommunity");
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a token that can be used to log onto Steam using steam-user.
|
||||
* @param {function} callback
|
||||
* @param {function} [callback]
|
||||
* @return Promise<{steamID: SteamID, accountName: string, webLogonToken: string}>
|
||||
*/
|
||||
SteamCommunity.prototype.getClientLogonToken = function(callback) {
|
||||
this.httpRequestGet({
|
||||
"uri": "https://steamcommunity.com/chat/clientjstoken",
|
||||
"json": true
|
||||
}, (err, res, body) => {
|
||||
if (err || res.statusCode != 200) {
|
||||
callback(err ? err : new Error('HTTP error ' + res.statusCode));
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => {
|
||||
let {jsonBody} = await this.httpRequest({
|
||||
method: 'GET',
|
||||
url: 'https://steamcommunity.com/chat/clientjstoken',
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
if (!body.logged_in) {
|
||||
if (!jsonBody.logged_in) {
|
||||
let e = new Error('Not Logged In');
|
||||
callback(e);
|
||||
this._notifySessionExpired(e);
|
||||
return;
|
||||
return reject(e);
|
||||
}
|
||||
|
||||
if (!body.steamid || !body.account_name || !body.token) {
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
if (!jsonBody.steamid || !jsonBody.account_name || !jsonBody.token) {
|
||||
return reject(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
"steamID": new SteamID(body.steamid),
|
||||
"accountName": body.account_name,
|
||||
"webLogonToken": body.token
|
||||
resolve({
|
||||
steamID: new SteamID(jsonBody.steamid),
|
||||
accountName: jsonBody.account_name,
|
||||
webLogonToken: jsonBody.token
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._setCookie = function(cookie, secure) {
|
||||
var protocol = secure ? "https" : "http";
|
||||
cookie.secure = !!secure;
|
||||
|
||||
if (cookie.domain) {
|
||||
this._jar.setCookie(cookie.clone(), protocol + '://' + cookie.domain);
|
||||
} else {
|
||||
this._jar.setCookie(cookie.clone(), protocol + "://steamcommunity.com");
|
||||
this._jar.setCookie(cookie.clone(), protocol + "://store.steampowered.com");
|
||||
this._jar.setCookie(cookie.clone(), protocol + "://help.steampowered.com");
|
||||
}
|
||||
/**
|
||||
* Sets a single cookie in our cookie jar.
|
||||
* @param {string} cookie
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._setCookie = function(cookie) {
|
||||
this._jar.add(cookie, 'steamcommunity.com');
|
||||
this._jar.add(cookie, 'store.steampowered.com');
|
||||
this._jar.add(cookie, 'help.steampowered.com');
|
||||
};
|
||||
|
||||
/**
|
||||
* Set one or more cookies in this SteamCommunity's cookie jar.
|
||||
* @param {string|string[]} cookies
|
||||
*/
|
||||
SteamCommunity.prototype.setCookies = function(cookies) {
|
||||
if (!Array.isArray(cookies)) {
|
||||
cookies = [cookies];
|
||||
}
|
||||
|
||||
cookies.forEach((cookie) => {
|
||||
var cookieName = cookie.trim().split('=')[0];
|
||||
let cookieName = cookie.match(/(.+)=/)[1];
|
||||
if (cookieName == 'steamLogin' || cookieName == 'steamLoginSecure') {
|
||||
this.steamID = new SteamID(cookie.match(/steamLogin(Secure)?=(\d+)/)[2]);
|
||||
this.steamID = new SteamID(cookie.match(/=(\d+)/)[1]);
|
||||
}
|
||||
|
||||
this._setCookie(Request.cookie(cookie), !!(cookieName.match(/^steamMachineAuth/) || cookieName.match(/Secure$/)));
|
||||
this._setCookie(cookie);
|
||||
});
|
||||
|
||||
// The account we're logged in as might have changed, so verify that our mobile access token (if any) is still valid
|
||||
@@ -178,278 +216,281 @@ SteamCommunity.prototype.setCookies = function(cookies) {
|
||||
this._verifyMobileAccessToken();
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getSessionID = function(host = "http://steamcommunity.com") {
|
||||
var cookies = this._jar.getCookieString(host).split(';');
|
||||
for(var i = 0; i < cookies.length; i++) {
|
||||
var match = cookies[i].trim().match(/([^=]+)=(.+)/);
|
||||
if(match[1] == 'sessionid') {
|
||||
return decodeURIComponent(match[2]);
|
||||
}
|
||||
SteamCommunity.prototype.getSessionID = function(domain = 'steamcommunity.com') {
|
||||
let sessionIdCookie = this._jar.cookies
|
||||
.filter(c => c.domain == domain)
|
||||
.find(c => c.name == 'sessionid');
|
||||
if (sessionIdCookie) {
|
||||
return sessionIdCookie.content;
|
||||
}
|
||||
|
||||
var sessionID = generateSessionID();
|
||||
this._setCookie(Request.cookie('sessionid=' + sessionID));
|
||||
// No cookie found? Generate a new session id
|
||||
let sessionID = require('crypto').randomBytes(12).toString('hex');
|
||||
this._setCookie(`sessionid=${sessionID}`);
|
||||
return sessionID;
|
||||
};
|
||||
|
||||
function generateSessionID() {
|
||||
return require('crypto').randomBytes(12).toString('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} pin
|
||||
* @param {function} [callback]
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
|
||||
var self = this;
|
||||
var sessionID = self.getSessionID();
|
||||
let sessionID = this.getSessionID();
|
||||
|
||||
this.httpRequestPost("https://steamcommunity.com/parental/ajaxunlock", {
|
||||
"json": true,
|
||||
"form": {
|
||||
"pin": pin,
|
||||
"sessionid": sessionID
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
let {jsonBody} = await this.httpRequest({
|
||||
method: 'POST',
|
||||
url: 'https://steamcommunity.com/parental/ajaxunlock',
|
||||
form: {
|
||||
pin: pin,
|
||||
sessionid: sessionID
|
||||
},
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
if (!jsonBody || typeof jsonBody.success !== 'boolean') {
|
||||
return reject('Invalid response');
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
if (!jsonBody.success) {
|
||||
switch (jsonBody.eresult) {
|
||||
case SteamCommunity.EResult.AccessDenied:
|
||||
return reject('Incorrect PIN');
|
||||
|
||||
if (!body || typeof body.success !== 'boolean') {
|
||||
callback("Invalid response");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!body.success) {
|
||||
switch (body.eresult) {
|
||||
case 15:
|
||||
callback("Incorrect PIN");
|
||||
break;
|
||||
|
||||
case 25:
|
||||
callback("Too many invalid PIN attempts");
|
||||
break;
|
||||
case SteamCommunity.EResult.LimitExceeded:
|
||||
return reject('Too many invalid PIN attempts');
|
||||
|
||||
default:
|
||||
callback("Error " + body.eresult);
|
||||
return reject('Error ' + jsonBody.eresult);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
}.bind(this), "steamcommunity");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {function} [callback]
|
||||
* @return Promise<object>
|
||||
*/
|
||||
SteamCommunity.prototype.getNotifications = function(callback) {
|
||||
var self = this;
|
||||
this.httpRequestGet({
|
||||
"uri": "https://steamcommunity.com/actions/GetNotificationCounts",
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => {
|
||||
let {jsonBody} = await this.httpRequest({
|
||||
method: 'GET',
|
||||
url: 'https://steamcommunity.com/actions/GetNotificationCounts',
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
if (!jsonBody || !jsonBody.notifications) {
|
||||
return reject(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
if (!body || !body.notifications) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
var notifications = {
|
||||
"trades": body.notifications[1] || 0,
|
||||
"gameTurns": body.notifications[2] || 0,
|
||||
"moderatorMessages": body.notifications[3] || 0,
|
||||
"comments": body.notifications[4] || 0,
|
||||
"items": body.notifications[5] || 0,
|
||||
"invites": body.notifications[6] || 0,
|
||||
let notifications = {
|
||||
trades: jsonBody.notifications[1] || 0,
|
||||
gameTurns: jsonBody.notifications[2] || 0,
|
||||
moderatorMessages: jsonBody.notifications[3] || 0,
|
||||
comments: jsonBody.notifications[4] || 0,
|
||||
items: jsonBody.notifications[5] || 0,
|
||||
invites: jsonBody.notifications[6] || 0,
|
||||
// dunno about 7
|
||||
"gifts": body.notifications[8] || 0,
|
||||
"chat": body.notifications[9] || 0,
|
||||
"helpRequestReplies": body.notifications[10] || 0,
|
||||
"accountAlerts": body.notifications[11] || 0
|
||||
gifts: jsonBody.notifications[8] || 0,
|
||||
chat: jsonBody.notifications[9] || 0,
|
||||
helpRequestReplies: jsonBody.notifications[10] || 0,
|
||||
accountAlerts: jsonBody.notifications[11] || 0
|
||||
};
|
||||
|
||||
callback(null, notifications);
|
||||
}, "steamcommunity");
|
||||
resolve(notifications);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {function} [callback]
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.resetItemNotifications = function(callback) {
|
||||
var self = this;
|
||||
this.httpRequestGet("https://steamcommunity.com/my/inventory", function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
await this.httpRequest({
|
||||
method: 'GET',
|
||||
url: 'https://steamcommunity.com/my/inventory',
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
callback(err || null);
|
||||
}, "steamcommunity");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {function} [callback]
|
||||
* @return Promise<{loggedIn: boolean, familyView: boolean}>
|
||||
*/
|
||||
SteamCommunity.prototype.loggedIn = function(callback) {
|
||||
this.httpRequestGet({
|
||||
"uri": "https://steamcommunity.com/my",
|
||||
"followRedirect": false,
|
||||
"checkHttpError": false
|
||||
}, function(err, response, body) {
|
||||
if(err || (response.statusCode != 302 && response.statusCode != 403)) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
return;
|
||||
return StdLib.Promises.callbackPromise(['loggedIn', 'familyView'], callback, false, async (resolve, reject) => {
|
||||
let result = await this.httpRequest({
|
||||
method: 'GET',
|
||||
url: 'https://steamcommunity.com/my',
|
||||
followRedirect: false,
|
||||
checkHttpError: false,
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
if (result.statusCode != 302 && result.statusCode != 403) {
|
||||
return reject(new Error(`HTTP error ${result.statusCode}`));
|
||||
}
|
||||
|
||||
if(response.statusCode == 403) {
|
||||
callback(null, true, true);
|
||||
return;
|
||||
if (result.statusCode == 403) {
|
||||
// TODO check response body to see if this is an akamai block
|
||||
return resolve({
|
||||
loggedIn: true,
|
||||
familyView: true
|
||||
});
|
||||
}
|
||||
|
||||
callback(null, !!response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^\/]+)\/?/), false);
|
||||
}, "steamcommunity");
|
||||
return resolve({
|
||||
loggedIn: !!result.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^/]+)\/?/),
|
||||
familyView: false
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {function} [callback]
|
||||
* @return Promise<{url: string, token: string}>
|
||||
*/
|
||||
SteamCommunity.prototype.getTradeURL = function(callback) {
|
||||
this._myProfile("tradeoffers/privacy", null, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
return StdLib.Promises.callbackPromise(['url', 'token'], callback, false, async (resolve, reject) => {
|
||||
let {textBody} = await this._myProfile('tradeoffers/privacy');
|
||||
|
||||
let match = textBody.match(/https?:\/\/(www.)?steamcommunity.com\/tradeoffer\/new\/?\?partner=\d+(&|&)token=([a-zA-Z0-9-_]+)/);
|
||||
if (!match) {
|
||||
return reject(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
var match = body.match(/https?:\/\/(www.)?steamcommunity.com\/tradeoffer\/new\/?\?partner=\d+(&|&)token=([a-zA-Z0-9-_]+)/);
|
||||
if (match) {
|
||||
var token = match[3];
|
||||
callback(null, match[0], token);
|
||||
} else {
|
||||
callback(new Error("Malformed response"));
|
||||
}
|
||||
}, "steamcommunity");
|
||||
let token = match[3];
|
||||
resolve({
|
||||
url: match[0],
|
||||
token
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param [callback]
|
||||
* @return Promise<{url: string, token: string}>
|
||||
*/
|
||||
SteamCommunity.prototype.changeTradeURL = function(callback) {
|
||||
this._myProfile("tradeoffers/newtradeurl", {"sessionid": this.getSessionID()}, (err, response, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
return StdLib.Promises.callbackPromise(['url', 'token'], callback, true, async (resolve, reject) => {
|
||||
let {textBody} = await this._myProfile('tradeoffers/newtradeurl', {sessionid: this.getSessionID()});
|
||||
|
||||
if (!textBody || typeof textBody !== 'string' || textBody.length < 3 || textBody.indexOf('"') !== 0) {
|
||||
return reject(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
if (!body || typeof body !== "string" || body.length < 3 || body.indexOf('"') !== 0) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
var newToken = body.replace(/"/g, ''); //"t1o2k3e4n" => t1o2k3e4n
|
||||
callback(null, "https://steamcommunity.com/tradeoffer/new/?partner=" + this.steamID.accountid + "&token=" + newToken, newToken);
|
||||
}, "steamcommunity");
|
||||
let newToken = textBody.replace(/"/g, ''); //"t1o2k3e4n" => t1o2k3e4n
|
||||
resolve({
|
||||
url: `https://steamcommunity.com/tradeoffer/new/?partner=${this.steamID.accountid}&token=${newToken}`,
|
||||
token: newToken
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear your profile name (alias) history.
|
||||
* @param {function} callback
|
||||
* @param {function} [callback]
|
||||
* @return Promise<void>
|
||||
*/
|
||||
SteamCommunity.prototype.clearPersonaNameHistory = function(callback) {
|
||||
this._myProfile("ajaxclearaliashistory/", {"sessionid": this.getSessionID()}, (err, res, body) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => {
|
||||
let {statusCode, textBody} = await this._myProfile('ajaxclearaliashistory/', {sessionid: this.getSessionID()});
|
||||
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
return callback(new Error("HTTP error " + res.statusCode));
|
||||
if (statusCode != 200) {
|
||||
return reject(new Error(`HTTP error ${statusCode}`));
|
||||
}
|
||||
|
||||
try {
|
||||
body = JSON.parse(body);
|
||||
callback(Helpers.eresultError(body.success));
|
||||
let body = JSON.parse(textBody);
|
||||
let err = Helpers.eresultError(body.success);
|
||||
return err ? reject(err) : resolve();
|
||||
} catch (ex) {
|
||||
return callback(new Error("Malformed response"));
|
||||
return reject(new Error('Malformed response'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._myProfile = function(endpoint, form, callback) {
|
||||
var self = this;
|
||||
|
||||
if (this._profileURL) {
|
||||
completeRequest(this._profileURL);
|
||||
} else {
|
||||
this.httpRequest("https://steamcommunity.com/my", {"followRedirect": false}, function(err, response, body) {
|
||||
if(err || response.statusCode != 302) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
var match = response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^\/]+)\/?/);
|
||||
if(!match) {
|
||||
callback(new Error("Can't get profile URL"));
|
||||
return;
|
||||
}
|
||||
|
||||
self._profileURL = match[1];
|
||||
setTimeout(function () {
|
||||
delete self._profileURL; // delete the cache
|
||||
}, 60000).unref();
|
||||
|
||||
completeRequest(match[1]);
|
||||
}, "steamcommunity");
|
||||
}
|
||||
|
||||
function completeRequest(url) {
|
||||
var options = endpoint.endpoint ? endpoint : {};
|
||||
options.uri = "https://steamcommunity.com" + url + "/" + (endpoint.endpoint || endpoint);
|
||||
|
||||
if (form) {
|
||||
options.method = "POST";
|
||||
options.form = form;
|
||||
options.followAllRedirects = true;
|
||||
} else if (!options.method) {
|
||||
options.method = "GET";
|
||||
}
|
||||
|
||||
self.httpRequest(options, callback, "steamcommunity");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an object whose keys are 64-bit SteamIDs, and whose values are values from the EFriendRelationship enum.
|
||||
* Therefore, you can deduce your friends or blocked list from this object.
|
||||
* @param {function} callback
|
||||
* @param {function} [callback]
|
||||
* @return Promise<object[]>
|
||||
*/
|
||||
SteamCommunity.prototype.getFriendsList = function(callback) {
|
||||
this.httpRequestGet({
|
||||
"uri": "https://steamcommunity.com/textfilter/ajaxgetfriendslist",
|
||||
"json": true
|
||||
}, (err, res, body) => {
|
||||
if (err) {
|
||||
callback(err ? err : new Error('HTTP error ' + res.statusCode));
|
||||
return;
|
||||
return StdLib.Promises.callbackPromise(['friends'], callback, false, async (resolve, reject) => {
|
||||
let {jsonBody} = await this.httpRequest({
|
||||
method: 'GET',
|
||||
url: 'https://steamcommunity.com/textfilter/ajaxgetfriendslist',
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
if (jsonBody.success != SteamCommunity.EResult.OK) {
|
||||
return reject(Helpers.eresultError(jsonBody.success));
|
||||
}
|
||||
|
||||
if (body.success != 1) {
|
||||
callback(Helpers.eresultError(body.success));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!body.friendslist || !body.friendslist.friends) {
|
||||
callback(new Error('Malformed response'));
|
||||
return;
|
||||
if (!jsonBody.friendslist || !jsonBody.friendslist.friends) {
|
||||
return reject(new Error('Malformed response'));
|
||||
}
|
||||
|
||||
const friends = {};
|
||||
body.friendslist.friends.forEach(friend => (friends[friend.ulfriendid] = friend.efriendrelationship));
|
||||
callback(null, friends);
|
||||
jsonBody.friendslist.friends.forEach(friend => (friends[friend.ulfriendid] = friend.efriendrelationship));
|
||||
resolve({friends});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @return Promise<{vanityURL: string, steamID: SteamID}>
|
||||
* @private
|
||||
*/
|
||||
SteamCommunity.prototype._resolveVanityURL = async function(url) {
|
||||
// Precede url param if only the vanity was provided
|
||||
if (!url.includes('steamcommunity.com')) {
|
||||
url = `https://steamcommunity.com/id/${url}`;
|
||||
}
|
||||
|
||||
// Make request to get XML data
|
||||
let {textBody} = await this._httpRequest({
|
||||
method: 'GET',
|
||||
url,
|
||||
source: 'steamcommunity'
|
||||
});
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
// Parse XML data returned from Steam into an object
|
||||
new xml2js.Parser().parseString(textBody, (err, parsed) => {
|
||||
if (err) {
|
||||
return reject(new Error('Couldn\'t parse XML response'));
|
||||
}
|
||||
|
||||
if (parsed.response && parsed.response.error) {
|
||||
return reject(new Error('Couldn\'t find Steam ID'));
|
||||
}
|
||||
|
||||
let steamID64 = parsed.profile.steamID64[0];
|
||||
let vanityURL = parsed.profile.customURL[0];
|
||||
|
||||
resolve({
|
||||
vanityURL,
|
||||
steamID: new SteamID(steamID64)
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
require('./components/login.js');
|
||||
require('./components/http.js');
|
||||
require('./components/chat.js');
|
||||
require('./components/profile.js');
|
||||
require('./components/market.js');
|
||||
require('./components/groups.js');
|
||||
require('./components/users.js');
|
||||
require('./components/sharedfiles.js');
|
||||
require('./components/inventoryhistory.js');
|
||||
require('./components/webapi.js');
|
||||
require('./components/twofactor.js');
|
||||
require('./components/confirmations.js');
|
||||
|
||||
31
package.json
31
package.json
@@ -1,14 +1,8 @@
|
||||
{
|
||||
"name": "steamcommunity",
|
||||
"version": "3.48.7",
|
||||
"version": "4.0.0-dev",
|
||||
"private": true,
|
||||
"description": "Provides an interface for logging into and interacting with the Steam Community website",
|
||||
"files": [
|
||||
"/classes",
|
||||
"/components",
|
||||
"/examples",
|
||||
"/resources",
|
||||
"/index.js"
|
||||
],
|
||||
"keywords": [
|
||||
"steam",
|
||||
"steam community"
|
||||
@@ -28,17 +22,22 @@
|
||||
"url": "https://github.com/DoctorMcKay/node-steamcommunity.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@doctormckay/user-agents": "^1.0.0",
|
||||
"async": "^2.6.3",
|
||||
"@doctormckay/stdlib": "^2.6.0",
|
||||
"cheerio": "0.22.0",
|
||||
"image-size": "^0.8.2",
|
||||
"request": "^2.88.0",
|
||||
"steam-session": "^1.9.1",
|
||||
"steam-totp": "^1.5.0",
|
||||
"steamid": "^1.1.3",
|
||||
"xml2js": "^0.6.2"
|
||||
"steam-session": "^1.2.4",
|
||||
"steam-totp": "^2.1.0",
|
||||
"steamid": "^2.0.0",
|
||||
"tough-cookie": "^4.0.0",
|
||||
"xml2js": "^0.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.31.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "npx eslint . --ext .js,.jsx,.ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* @enum EChatState
|
||||
*/
|
||||
module.exports = {
|
||||
"Offline": 0,
|
||||
"LoggingOn": 1,
|
||||
"LogOnFailed": 2,
|
||||
"LoggedOn": 3,
|
||||
|
||||
"0": "Offline",
|
||||
"1": "LoggingOn",
|
||||
"2": "LogOnFailed",
|
||||
"3": "LoggedOn"
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* @enum EConfirmationType
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* @enum EFriendRelationship
|
||||
*/
|
||||
module.exports = {
|
||||
module.exports = {
|
||||
"None": 0,
|
||||
"Blocked": 1,
|
||||
"RequestRecipient": 2,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* @enum EPersonaState
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* @enum EPersonaStateFlag
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/* eslint-disable */
|
||||
// Auto-generated by generate-enums script on Thu Jul 29 2021 04:43:52 GMT-0400 (Eastern Daylight Time)
|
||||
|
||||
/**
|
||||
* @enum EResult
|
||||
*/
|
||||
@@ -129,7 +132,8 @@ module.exports = {
|
||||
"DeniedDueToCommunityCooldown": 116,
|
||||
"NoLauncherSpecified": 117,
|
||||
"MustAgreeToSSA": 118,
|
||||
"ClientNoLongerSupported": 119,
|
||||
"ClientNoLongerSupported": 119, // obsolete
|
||||
"LauncherMigrated": 119,
|
||||
|
||||
// Value-to-name mapping for convenience
|
||||
"0": "Invalid",
|
||||
@@ -250,5 +254,5 @@ module.exports = {
|
||||
"116": "DeniedDueToCommunityCooldown",
|
||||
"117": "NoLauncherSpecified",
|
||||
"118": "MustAgreeToSSA",
|
||||
"119": "ClientNoLongerSupported",
|
||||
"119": "LauncherMigrated",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user