mirror of
https://github.com/DoctorMcKay/node-steamcommunity.git
synced 2025-03-14 06:50:06 +08:00
Updated confirmations.js methods to use new http interface
This commit is contained in:
parent
028ee43bda
commit
c1901d5f55
@ -1,3 +1,5 @@
|
||||
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,47 +1,34 @@
|
||||
const Cheerio = require('cheerio');
|
||||
const StdLib = require('@doctormckay/stdlib');
|
||||
const SteamTotp = require('steam-totp');
|
||||
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
const CConfirmation = require('../classes/CConfirmation.js');
|
||||
var EConfirmationType = require('../resources/EConfirmationType.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,7 +41,7 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) {
|
||||
icon: conf.icon || ''
|
||||
}));
|
||||
|
||||
callback(null, confs);
|
||||
resolve({confirmations: confs});
|
||||
});
|
||||
};
|
||||
|
||||
@ -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'));
|
||||
}
|
||||
|
||||
let $ = Cheerio.load(body);
|
||||
let offer = $('.tradeoffer');
|
||||
if (offer.length < 1) {
|
||||
callback(null, null);
|
||||
return;
|
||||
return resolve({offerID: null});
|
||||
}
|
||||
|
||||
callback(null, offer.attr('id').split('_')[1]);
|
||||
resolve({offerID: offer.attr('id').split('_')[1]});
|
||||
});
|
||||
};
|
||||
|
||||
@ -108,42 +90,25 @@ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, ca
|
||||
* @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,94 +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) {
|
||||
this._usedConfTimes = this._usedConfTimes || [];
|
||||
|
||||
let doConfirmation = () => {
|
||||
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();
|
||||
}
|
||||
|
||||
this._timeOffset = offset;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let offset = this._timeOffset;
|
||||
let time = SteamTotp.time(offset);
|
||||
this.getConfirmations(time, SteamTotp.getConfirmationKey(identitySecret, time, 'conf'), (err, confs) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
let key = SteamTotp.getConfirmationKey(identitySecret, time, 'list');
|
||||
let {confirmations} = await this.getConfirmations(time, key);
|
||||
|
||||
let conf = confs.find(conf => conf.creator == objectID);
|
||||
if (!conf) {
|
||||
callback(new Error('Could not find confirmation for object ' + objectID));
|
||||
return;
|
||||
}
|
||||
let conf = confirmations.find(conf => conf.creator == objectID);
|
||||
if (!conf) {
|
||||
return reject(new Error(`Could not find confirmation for object ${objectID}`));
|
||||
}
|
||||
|
||||
// make sure we don't reuse the same time
|
||||
let localOffset = 0;
|
||||
do {
|
||||
time = SteamTotp.time(offset) + localOffset++;
|
||||
} while (this._usedConfTimes.indexOf(time) != -1);
|
||||
// make sure we don't reuse the same time
|
||||
let localOffset = 0;
|
||||
do {
|
||||
time = SteamTotp.time(offset) + localOffset++;
|
||||
} while (this._usedConfTimes.includes(time));
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
conf.respond(time, SteamTotp.getConfirmationKey(identitySecret, time, 'allow'), true, callback);
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof this._timeOffset !== 'undefined') {
|
||||
// time offset is already known and saved
|
||||
doConfirmation();
|
||||
} else {
|
||||
SteamTotp.getTimeOffset((err, offset) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
this._timeOffset = offset;
|
||||
doConfirmation();
|
||||
|
||||
setTimeout(() => {
|
||||
// Delete the saved time offset after 12 hours because why not
|
||||
delete this._timeOffset;
|
||||
}, 1000 * 60 * 60 * 12).unref();
|
||||
});
|
||||
}
|
||||
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) {
|
||||
this.getConfirmations(time, confKey, (err, confs) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
this.respondToConfirmation(confs.map(conf => conf.id), confs.map(conf => conf.key), time, allowKey, true, (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');
|
||||
}
|
||||
@ -253,8 +203,8 @@ function request(community, url, key, time, tag, params, json, callback) {
|
||||
|
||||
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') {
|
||||
@ -263,12 +213,6 @@ function request(community, url, key, time, tag, params, json, callback) {
|
||||
req.form = params;
|
||||
}
|
||||
|
||||
community.httpRequest(req, (err, response, body) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, body);
|
||||
}, 'steamcommunity');
|
||||
let result = await community.httpRequest(req);
|
||||
return result.jsonBody || result.textBody;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
const {HttpResponse} = require('@doctormckay/stdlib/http'); // eslint-disable-line
|
||||
const {betterPromise} = require('@doctormckay/stdlib/promises');
|
||||
|
||||
const SteamCommunity = require('../index.js');
|
||||
|
||||
@ -20,67 +21,50 @@ const SteamCommunity = require('../index.js');
|
||||
* @return {Promise<HttpResponse>}
|
||||
*/
|
||||
SteamCommunity.prototype.httpRequest = function(options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return betterPromise(async (resolve, reject) => {
|
||||
let requestID = ++this._httpRequestID;
|
||||
let source = options.source || '';
|
||||
|
||||
let continued = false;
|
||||
|
||||
let continueRequest = async (err) => {
|
||||
if (continued) {
|
||||
return;
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
continued = true;
|
||||
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
|
||||
});
|
||||
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
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;
|
||||
|
||||
/** @var {HttpResponse} result */
|
||||
let result;
|
||||
this.emit('postHttpRequest', {
|
||||
requestID,
|
||||
source,
|
||||
options,
|
||||
response: result,
|
||||
body: result.textBody,
|
||||
error: httpError || communityError || tradeError || jsonError || null,
|
||||
httpError,
|
||||
communityError,
|
||||
tradeError,
|
||||
jsonError
|
||||
});
|
||||
|
||||
try {
|
||||
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
|
||||
});
|
||||
} catch (ex) {
|
||||
return reject(ex);
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
"url": "https://github.com/DoctorMcKay/node-steamcommunity.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@doctormckay/stdlib": "^2.5.0",
|
||||
"@doctormckay/stdlib": "^2.6.0",
|
||||
"cheerio": "0.22.0",
|
||||
"image-size": "^0.8.2",
|
||||
"request": "^2.88.0",
|
||||
|
Loading…
Reference in New Issue
Block a user