Compare commits

...

123 Commits

Author SHA1 Message Date
Alex Corn
d9b6b55efc 3.41.7 2020-09-23 18:11:33 -04:00
Alex Corn
0fc62d2966 Merge pull request #257 from timi-owo/master
Fixed 'language' was missing from the 'CSteamUser.getInventoryContents'
2020-09-23 17:46:59 -04:00
timi-owo
5daf5f712a Fixed param 'language' was missing when calling 'CSteamUser.getInventoryContents' method 2020-09-24 01:44:32 +08:00
Alex Corn
737aa9dc41 Added comment 2020-07-01 00:52:15 -04:00
Alex Corn
07fb26047b 3.41.6 2020-06-30 22:36:48 -04:00
Alex Corn
2f9b24c447 Only attempt to delete this.currency if it actually exists 2020-06-30 22:24:04 -04:00
Alex Corn
211c625da0 3.41.5 2020-06-26 03:45:17 -04:00
Alex Corn
2e61a6ed70 Fixed editProfile and profileSettings for Steam update 2020-06-26 03:44:58 -04:00
Alex Corn
f0a3cb27f8 Merge pull request #250 from itsjfx/open-booster-pack-patch
Add openBoosterPack
2020-05-28 22:30:35 -04:00
Alex Corn
6ed33a100e 3.41.4 2020-05-28 00:15:39 -04:00
Alex Corn
d41a52638b Prevent timeOffset delete timeout from holding app open 2020-05-28 00:15:20 -04:00
jfx
58516d4ed1 Add openBoosterPack 2020-05-23 16:24:20 +10:00
Alex Corn
3f99c718d4 Modernized disable_twofactor example script 2020-04-04 21:53:34 -04:00
Alex Corn
b92c5d852d Modernized enable_twofactor example script 2020-04-04 21:53:32 -04:00
Alex Corn
f5da011cde Create FUNDING.yml 2020-03-10 03:51:16 -04:00
Alex Corn
3633ab032c 3.41.3 2019-11-08 03:33:06 -05:00
Alex Corn
76df74d724 Added hidden parameter to CSteamGroup#postAnnouncement 2019-11-08 03:33:00 -05:00
Alex Corn
8b5cd59021 v3.41.2 2019-11-08 03:27:57 -05:00
Alex Corn
25adcb101b 3.41.1 2019-11-08 03:24:42 -05:00
Alex Corn
d7ecad670d Merge pull request #237 from Heartz66/patch-1
Fix wrong date
2019-11-08 03:23:27 -05:00
Heartz66
8225ea5127 Fix wrong date
https://github.com/DoctorMcKay/node-steamcommunity/issues/236
2019-11-06 19:43:45 +01:00
Alex Corn
59bf4f1e92 Merge pull request #233 from Revadike/patch-1
Optional hidden parameter to postGroupAnnouncement
2019-10-15 01:13:03 +02:00
Revadike
a3468bf99b Optional hidden parameter to postGroupAnnouncement 2019-10-14 04:28:53 +02:00
Alex Corn
e299623452 Use different filenames for different images, similar to how Steam does it 2019-09-25 05:33:14 -04:00
Alex Corn
29b31009ce Merge remote-tracking branch 'origin/master' 2019-09-25 05:30:14 -04:00
Alex Corn
d4d0d7da26 3.41.0 2019-09-24 04:07:34 -04:00
Alex Corn
3c8488d441 Added method sendImageToUser 2019-09-24 04:06:37 -04:00
Alex Corn
3ea0995415 3.40.2 2019-09-21 04:27:55 -04:00
Alex Corn
21c4ba3e27 Restore cache_expiration from game-specific data for backwards compatibility
(Fixes #229)
2019-09-21 04:27:39 -04:00
Alex Corn
4d4df7c2a5 3.40.1 2019-09-07 05:06:06 -04:00
Alex Corn
aa0d4d32b7 Removed stats-reporter 2019-09-07 05:05:46 -04:00
Alex Corn
444a6d238d Merge pull request #225 from Revadike/patch-1
Added userID guard
2019-06-17 03:18:06 -04:00
Revadike
98373708e5 Added userID guard
Because I got this:
```
 2019-06-12 | 01:22:30.151 | ERROR | crashBot                       | Fatal error: TypeError: Cannot read property 'getSteamID64' of null
    at get (node_modules\steamcommunity\components\users.js:416:60)
    at SteamCommunity.getUserInventoryContents (node_modules\steamcommunity\components\users.js:412:2)
    at unpackSteamBoosterPacks (index.js:3588:21)
    at cleanupSteamInventory (index.js:3583:5)
    at SteamUser.refreshSteamWebSession (index.js:857:38)
    at Object.onceWrapper (events.js:273:13)
    at SteamUser.emit (events.js:182:13)
    at SteamUser.gotSteamWebSession (index.js:1754:16)
    at SteamUser.emit (events.js:182:13)
    at _apiRequest (node_modules\steam-user\components\web.js:61:9)
    at IncomingMessage.<anonymous> (node_modules\steam-user\components\webapi.js:82:4)
    at IncomingMessage.emit (events.js:187:15)
    at endReadableNT (_stream_readable.js:1094:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)
```
2019-06-17 02:43:07 +02:00
Alex Corn
0df2662053 3.40.0 2019-04-19 01:28:43 -04:00
Alex Corn
a46ed17415 Added clearPersonaNameHistory 2019-04-19 01:20:01 -04:00
Alex Corn
fbfff1398b Merge remote-tracking branch 'origin/master'
# Conflicts:
#	.idea/encodings.xml
2019-02-14 00:29:44 -05:00
Alex Corn
224b7b85e0 3.39.0 2019-02-13 17:59:41 -05:00
Alex Corn
22d34b1faf Added Steam ID to output of getClientLogonToken 2019-02-13 17:59:33 -05:00
Alex Corn
cfd932dc72 Changed output format of getClientLogonToken 2019-02-13 17:42:28 -05:00
Alex Corn
4710afa28b Added getClientLogonToken 2019-02-13 17:40:45 -05:00
Alex Corn
1180eb9d3f Deprecated webchat methods 2019-02-13 17:35:39 -05:00
Alex Corn
3ca85bedc2 Merge branch 'master' of ssh://github.com/DoctorMcKay/node-steamcommunity 2019-02-13 17:28:57 -05:00
Alex Corn
e05efabcb9 Merge pull request #211 from Hongarc/master
Add comment group
2019-02-12 21:35:58 -05:00
Alex Corn
6cd06c3adf Updated module type 2019-02-06 19:54:04 -05:00
Alex Corn
d35e3c107c Make sure CConfirmation.{id, creator} are always strings 2019-02-06 19:36:01 -05:00
Alex Corn
4e90811008 Merge pull request #213 from Royalgamer06/patch-2
Package 'fs' never used
2018-11-19 14:54:33 -05:00
Royalgamer06
018d3de5a2 Package 'fs' never used 2018-11-19 20:48:57 +01:00
Alex Corn
3949abe09f 3.38.0 2018-11-01 01:20:31 -04:00
Alex Corn
62394b9ffe Added ability to change friends list privacy 2018-11-01 01:20:19 -04:00
Alex Corn
ebac6c97ad Added IDE config to repo 2018-10-31 03:47:55 -04:00
Hongarc
a0ad33d5eb Add comment group 2018-10-20 13:29:52 +07:00
Alex Corn
b7a3e51bfc Merge pull request #205 from waylaidwanderer/master
Add the ability to set custom mobile user-agent
2018-07-11 02:31:04 -04:00
Joel
bf13224cbf Add the ability to set custom mobile user-agent 2018-07-10 23:30:12 -07:00
Alex Corn
117374a4c5 Updated Chrome user-agent 2018-07-11 02:22:39 -04:00
Alex Corn
aa84505132 3.37.1 2018-07-11 02:12:45 -04:00
Alex Corn
bb97565f2a Fixed steamID not being properly set 2018-07-11 02:12:37 -04:00
Alex Corn
7462e4b767 3.37.0 2018-07-11 00:00:16 -04:00
Alex Corn
adbf1d47a7 Added postProfileStatus and deleteProfileStatus (closes #200) 2018-07-10 23:59:57 -04:00
Alex Corn
8c4fde720a Updated EResult 2018-07-10 23:53:05 -04:00
Alex Corn
458d7b6db1 Renamed getProfileBackground to getUserProfileBackground and added it to CSteamUser 2018-07-10 23:28:26 -04:00
Alex Corn
411d74036f Updated getProfileBackground output a bit 2018-07-10 23:24:41 -04:00
Mert
1b3117b8dc Add getProfileBackground method
Adds ability to get URL of Steamcommunity profile background.
2018-07-10 23:18:03 -04:00
Alex Corn
6eec88d196 3.36.2 2018-05-18 17:32:48 -04:00
Alex Corn
3921ce17d0 Follow redirects when posting to own profile (fixes #178) 2018-05-18 17:32:33 -04:00
Alex Corn
4441d6f6e0 Bumped required request version 2018-05-18 17:29:14 -04:00
Alex Corn
870de6817e 3.36.1 2018-04-15 10:43:14 -04:00
Alex Corn
d5dbb230df Removed extra import 2018-04-15 10:43:05 -04:00
Alex Corn
5e9c06bdc4 3.36.0 2018-04-11 00:27:57 -04:00
Alex Corn
8d163d0d85 Added privacy controls for gameDetails and playtime 2018-04-11 00:27:10 -04:00
Alex Corn
051d376508 3.35.2 2018-04-10 01:08:14 -04:00
Alex Corn
913dff68ef Fixed crashes if somehow market items are really broken (fixes #197) 2018-04-10 01:06:52 -04:00
Alex Corn
754847ee88 Make CMarketItem not broken for non-CSGO apps (fixes #196) 2018-04-10 01:01:03 -04:00
Alex Corn
645e03b89c 3.35.1 2018-04-01 23:36:04 -04:00
Alex Corn
aa302bc09e Fixed various profile url requests 2018-04-01 23:35:56 -04:00
Alex Corn
1c0f8d4bca 3.35.0 2018-04-01 03:39:00 -04:00
Alex Corn
a7bd694f4b Added inventory gift redemption 2018-04-01 03:38:45 -04:00
Alex Corn
9e380e71f5 jsdoc 2018-04-01 03:31:07 -04:00
Alex Corn
e97e63fe6d Added ability to get gem value for an item and to turn items into gems 2018-04-01 03:22:00 -04:00
Alex Corn
f6c7c74ab6 Fixed Node.js process being kept running unnecessarily 2018-04-01 03:15:13 -04:00
Alex Corn
8846082f60 Fixed getMarketApps not returning all apps 2018-04-01 02:51:46 -04:00
Alexander Corn
eb609b5c42 Added some more explicit error handling if we try to do things while logged out 2018-02-27 01:40:59 -05:00
Alex Corn
238a36de0f v3.34.1 2018-01-22 02:02:56 -05:00
Alex Corn
c38111b61a Merge pull request #189 from Aareksio/patch-1
Fix infinite loop in getWebApiKey
2018-01-22 01:48:17 -05:00
Arkadiusz Sygulski
0eb27a15d2 Fix infinite loop in getWebApiKey
Fix infinite loop in getWebApiKey when trying to get web api key on account without email address confirmed
2018-01-22 03:48:45 +01:00
Alexander Corn
0ab3a94493 Use writeFileSync instead of writeFile without callback 2018-01-16 14:40:07 -05:00
Alexander Corn
7c2ec65b8c Add .editorconfig 2017-12-30 02:25:07 -05:00
Alexander Corn
8e761ffe6c 3.34.0 2017-12-28 00:34:23 -05:00
Alexander Corn
06f04887fd Updated CONTRIBUTING.md 2017-12-28 00:34:15 -05:00
Alexander Corn
e5f986a8bf Added author info 2017-12-28 00:33:01 -05:00
Alexander Corn
5da6e40ebb Fixed some code style issues 2017-12-28 00:32:06 -05:00
Alex Corn
0b8dd57a40 Merge pull request #186 from hell00world/master
Added changeTradeURL method
2017-12-28 00:21:00 -05:00
hell00world
6f8456882a Added changeTradeURL method 2017-12-28 00:19:57 +01:00
Alex Corn
0b57b47ecb Minor tweaks 2017-12-26 16:39:23 -05:00
Alex Corn
b5f50a6614 Style update 2017-12-26 16:34:19 -05:00
Alex Corn
4257140c15 Merge remote-tracking branch 'origin/master' 2017-12-26 16:34:02 -05:00
Alex Corn
724badd175 Merge pull request #163 from potato20/disable-mobile-flag
Flag to disable mobile login
2017-12-26 16:33:56 -05:00
Alex Corn
a9455b0ff6 Style updates, use HTTPS, coerce steamids to SteamId objects 2017-12-26 16:32:21 -05:00
Alex Corn
b3dfdd47d8 Fixed missing brace 2017-12-26 16:32:04 -05:00
Alex Corn
1efac6bc70 Merge pull request #159 from Jarzon/groupcomments
Steam group comments
2017-12-26 16:28:16 -05:00
Alex Corn
eacb85c711 3.33.2 2017-11-30 08:59:58 -05:00
Alex Corn
8eab34bf18 Fixed errors not being handled in getTradeURL 2017-11-30 08:59:49 -05:00
Alexander Corn
b45e8d97ed 3.33.1 2017-10-21 16:52:11 -04:00
Alex Corn
6abb14a9bc Merge pull request #183 from borianm/patch-1
Fix for issue #182
2017-10-21 16:48:23 -04:00
Alexander Corn
6049d7a259 Added deprecation notice to getInventoryHistory 2017-10-20 19:28:26 -04:00
AirboN
e0320b27d7 Fixed issue with undefined author in events 2017-10-17 00:48:06 +02:00
AirboN
7986a87b3f Fixed issue with undefined author in events 2017-10-17 00:42:26 +02:00
Alexander Corn
cf7e5f8995 3.33.0 2017-10-06 12:01:10 -04:00
Alexander Corn
a03dad9507 Added getTradeURL method 2017-10-02 14:55:29 -04:00
Jason Vaillancourt
dc2e5d09a7 Merge branch 'master' into groupcomments 2017-08-20 20:13:41 -04:00
Jarzon
3bba7025d4 Changes for code consistency 2017-03-15 13:08:22 -04:00
Jarzon
e2f94cb1e7 Convert the number to a string 2017-03-15 12:55:48 -04:00
Jarzon
91285ba1c7 Merge branch 'master' into groupcomments 2017-03-04 09:04:24 -05:00
Potato 2.0
c6ab392c37 Add disableMobile flag to login() so user has option to not pretend we're logging in from a phone 2017-02-18 14:13:15 -05:00
Jarzon
530bbcb9eb Pass a Error obj to the callback 2017-02-08 15:22:50 -05:00
Jarzon
f7ebe68bd8 Adding .deleteComment() 2017-02-08 15:08:34 -05:00
Jarzon
e2dae00af2 Simplifying the request's options 2017-02-07 18:34:12 -05:00
Jarzon
bbd80d4154 Fixing not getting the html in the comment 2017-02-02 07:53:18 -05:00
Jarzon
c180557f52 Using decodeSteamTime() to parse date
Adding to .decodeSteamTime() the ability to parse steam relative time
2017-02-01 13:30:38 -05:00
Jarzon
b3174b9f0b Revert using chrono to parse the date 2017-02-01 12:03:34 -05:00
Jarzon
12f1ddf6a0 Missing header Content-Type 2017-02-01 11:27:58 -05:00
Jarzon
db3aa4ace8 Catch the community id and the custom url of the author 2017-01-31 08:48:38 -05:00
Jarzon
f1b742dff4 Adding Chrono to parse relative date 2017-01-31 06:26:11 -05:00
Jarzon
444649ee8c Adding .getAllComments() 2017-01-31 06:16:38 -05:00
41 changed files with 1386 additions and 339 deletions

13
.editorconfig Normal file
View File

@@ -0,0 +1,13 @@
root = true
[*]
charset = utf-8
indent_style = tab
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: DoctorMcKay
custom: 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=N36YVAT42CZ4G&item_name=node%2dsteamcommunity&currency_code=USD'

52
.gitignore vendored
View File

@@ -1,3 +1,53 @@
node_modules/*
test.js
.idea/
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
.idea/httpRequests/
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
cmake-build-release/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

1
.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
node-steamcommunity

13
.idea/codeStyleSettings.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
</value>
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
</component>
</project>

18
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,18 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

3
.idea/copyright/profiles_settings.xml generated Normal file
View File

@@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

6
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

View File

@@ -0,0 +1,11 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<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="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" />
</profile>
</component>

6
.idea/jsLibraryMappings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>

6
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/steamcommunity.iml" filepath="$PROJECT_DIR$/.idea/steamcommunity.iml" />
</modules>
</component>
</project>

8
.idea/node-steamcommunity.iml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

9
.idea/steamcommunity.iml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

4
.idea/watcherTasks.xml generated Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="Babel" />
</project>

View File

@@ -28,6 +28,7 @@ Submitting a pull request? Great! Thanks for contributing your time and code! Pl
- Tabs for indentation
- camelCase for variables and functions
- Opening braces on the same line as the if/for/while statement
- No omitting unnecessary braces
- etc.
- Please avoid breaking changes. If you make a breaking change that can be done in a backwards-compatible manner, I won't accept it.
- Please don't increment the version number in `package.json`. I'll do that myself when I publish it to npm.

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015 Alexander Corn
Copyright (c) 2015 Alex Corn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -9,8 +9,6 @@ This module provides an easy interface for the Steam Community website. This mod
It supports Steam Guard and CAPTCHAs.
This reports anonymous usage statistics to the author. [See here](https://github.com/DoctorMcKay/node-stats-reporter) for more information.
**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/).
Such issues may be ignored!**

View File

@@ -5,9 +5,9 @@ module.exports = CConfirmation;
function CConfirmation(community, data) {
Object.defineProperty(this, "_community", {"value": community});
this.id = data.id;
this.id = data.id.toString();
this.type = data.type;
this.creator = data.creator;
this.creator = data.creator.toString();
this.key = data.key;
this.title = data.title;
this.receiving = data.receiving;

View File

@@ -66,11 +66,31 @@ function CEconItem(item, description, contextID) {
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 After ') == 0);
if (description) {
let date = new Date(description.value.substring(15).replace(/[,()]/g, ''));
if (date) {
this.cache_expiration = date.toISOString();
}
}
}
// If we have item_expiration, also set cache_expiration to the same value
if (this.item_expiration) {
this.cache_expiration = this.item_expiration;
}
if (this.actions === "") {
this.actions = [];
}
delete this.currency;
// One wouldn't think that we need this if statement, but apparently v8 has a weird bug/quirk where deleting a
// property results in greatly increased memory usage. Because that makes sense.
if (this.currency) {
delete this.currency;
}
}
CEconItem.prototype.getImageURL = function() {

View File

@@ -12,7 +12,7 @@ SteamCommunity.prototype.getMarketItem = function(appid, hashName, currency, cal
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."));
@@ -35,7 +35,7 @@ function CMarketItem(appid, hashName, community, body, $) {
this._hashName = hashName;
this._community = community;
this._$ = $;
this._country = "US";
var match = body.match(/var g_strCountryCode = "([^"]+)";/);
if(match) {
@@ -47,14 +47,14 @@ function CMarketItem(appid, hashName, community, body, $) {
if(match) {
this._language = match[1];
}
this.commodity = false;
var match = body.match(/Market_LoadOrderSpread\(\s*(\d+)\s*\);/);
match = body.match(/Market_LoadOrderSpread\(\s*(\d+)\s*\);/);
if(match) {
this.commodity = true;
this.commodityID = parseInt(match[1], 10);
}
this.medianSalePrices = null;
match = body.match(/var line1=([^;]+);/);
if(match) {
@@ -78,7 +78,8 @@ function CMarketItem(appid, hashName, community, body, $) {
if (match) {
try {
this.assets = JSON.parse(match[1]);
this.assets = this.assets['730']['2'];
this.assets = this.assets[appid];
this.assets = this.assets[Object.keys(this.assets)[0]];
this.firstAsset = this.assets[Object.keys(this.assets)[0]];
} catch (e) {
// ignore
@@ -112,29 +113,29 @@ CMarketItem.prototype.updatePriceForCommodity = function(currency, callback) {
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);

View File

@@ -3,26 +3,26 @@ var Cheerio = require('cheerio');
SteamCommunity.prototype.marketSearch = function(options, callback) {
var qs = {};
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) {
continue;
}
// This is a tag
qs['category_' + qs.appid + '_' + i + '[]'] = 'tag_' + options[i];
}
}
}
qs.start = 0;
qs.count = 100;
qs.sort_column = 'price';
@@ -80,10 +80,10 @@ SteamCommunity.prototype.marketSearch = function(options, callback) {
function CMarketSearchResult(row) {
var 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);
}

View File

@@ -78,8 +78,8 @@ CSteamGroup.prototype.getAllAnnouncements = function(time, callback) {
this._community.getAllGroupAnnouncements(this.steamID, time, callback);
};
CSteamGroup.prototype.postAnnouncement = function(headline, content, callback) {
this._community.postGroupAnnouncement(this.steamID, headline, content, callback);
CSteamGroup.prototype.postAnnouncement = function(headline, content, hidden, callback) {
this._community.postGroupAnnouncement(this.steamID, headline, content, hidden, callback);
};
CSteamGroup.prototype.editAnnouncement = function(annoucementID, headline, content, callback) {
@@ -114,6 +114,19 @@ CSteamGroup.prototype.getHistory = function(page, callback) {
this._community.getGroupHistory(this.steamID, page, callback);
};
CSteamGroup.prototype.getAllComments = function(from, count, callback) {
this._community.getAllGroupComments(this.steamID, from, count, callback);
};
CSteamGroup.prototype.deleteComment = function(cid, callback) {
this._community.deleteGroupComment(this.steamID, cid, callback);
};
CSteamGroup.prototype.comment = function(message, callback) {
this._community.postGroupComment(this.steamID, message, callback);
};
/**
* Get requests to join this restricted group.
* @param {function} callback - First argument is null/Error, second is array of SteamID objects

View File

@@ -7,29 +7,29 @@ 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 === '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("http://steamcommunity.com/" + (typeof id === 'string' ? "id/" + id : "profiles/" + id.toString()) + "/?xml=1", function(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"));
return;
}
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) {
@@ -43,7 +43,7 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) {
callback(new Error("No valid response"));
return;
}
callback(null, new CSteamUser(self, result.profile, customurl));
});
}, "steamcommunity");
@@ -51,7 +51,7 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) {
function CSteamUser(community, userData, customurl) {
this._community = community;
this.steamID = new SteamID(userData.steamID64[0]);
this.name = processItem('steamID');
this.onlineState = processItem('onlineState');
@@ -67,9 +67,15 @@ function CSteamUser(community, userData, customurl) {
this.tradeBanState = processItem('tradeBanState', 'None');
this.isLimitedAccount = processItem('isLimitedAccount') == 1;
this.customURL = processItem('customURL', customurl);
if(this.visibilityState == 3) {
this.memberSince = new Date(processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, "$1"));
let memberSinceValue = processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, "$1");
if (memberSinceValue.indexOf(',') === -1) {
memberSinceValue += ', ' + new Date().getFullYear();
}
this.memberSince = new Date(memberSinceValue);
this.location = processItem('location');
this.realName = processItem('realname');
this.summary = processItem('summary');
@@ -79,19 +85,19 @@ function CSteamUser(community, userData, customurl) {
this.realName = null;
this.summary = null;
}
// Maybe handle mostPlayedGames?
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]);
}
return new SteamID(group.groupID64[0]);
});
}
@@ -110,7 +116,7 @@ CSteamUser.getAvatarURL = function(hash, size, protocol) {
protocol = protocol || 'http://';
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";
@@ -177,8 +183,27 @@ CSteamUser.prototype.getInventory = function(appID, contextID, tradableOnly, cal
* @param {int} appID - The Steam application ID of the game for which you want an inventory
* @param {int} contextID - The ID of the "context" within the game you want to retrieve
* @param {boolean} tradableOnly - true to get only tradable items and currencies
* @param {string} [language] - The language of item descriptions to return. Omit for default (which may either be English or your account's chosen language)
* @param callback
*/
CSteamUser.prototype.getInventoryContents = function(appID, contextID, tradableOnly, callback) {
this._community.getUserInventoryContents(this.steamID, appID, contextID, tradableOnly, callback);
CSteamUser.prototype.getInventoryContents = function(appID, contextID, tradableOnly, language, callback) {
this._community.getUserInventoryContents(this.steamID, appID, contextID, tradableOnly, language, callback);
};
/**
* Get the background URL of user's profile.
* @param {function} callback
*/
CSteamUser.prototype.getProfileBackground = function(callback) {
this._community.getUserProfileBackground(this.steamID, callback);
};
/**
* Upload an image to Steam and send it to the target user over chat.
* @param {Buffer} imageContentsBuffer - The image contents, as a Buffer
* @param {{spoiler?: boolean}} [options]
* @param {function} callback
*/
CSteamUser.prototype.sendImage = function(imageContentsBuffer, options, callback) {
this._community.sendImageToUser(this.steamID, imageContentsBuffer, options, callback);
};

View File

@@ -5,17 +5,22 @@ 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) {
@@ -32,7 +37,7 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) {
self.emit('debug', "Cannot get oauth token: " + err.message);
return;
}
self.httpRequestPost({
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logon/v1",
"form": {
@@ -48,7 +53,7 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) {
setTimeout(self.chatLogon.bind(self), 5000);
return;
}
if(body.error != 'OK') {
self.chatState = SteamCommunity.ChatState.LogOnFailed;
self.emit('chatLogOnFailed', new Error(body.error), false);
@@ -56,7 +61,7 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) {
setTimeout(self.chatLogon.bind(self), 5000);
return;
}
self._chat = {
"umqid": body.umqid,
"message": body.message,
@@ -64,9 +69,9 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) {
"interval": interval,
"uiMode": uiMode
};
self.chatFriends = {};
self.chatState = SteamCommunity.ChatState.LoggedOn;
self.emit('chatLoggedOn');
self._chatPoll();
@@ -74,20 +79,27 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) {
});
};
/**
* @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;
@@ -105,12 +117,12 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback)
if(!callback) {
return;
}
if (err) {
callback(err);
return;
}
if(body.error != 'OK') {
callback(new Error(body.error));
} else {
@@ -119,6 +131,9 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback)
}, "steamcommunity");
};
/**
* @deprecated No support for new Steam chat. Use steam-user instead.
*/
SteamCommunity.prototype.chatLogoff = function() {
var self = this;
this.httpRequestPost({
@@ -141,9 +156,12 @@ SteamCommunity.prototype.chatLogoff = function() {
}, "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",
@@ -161,9 +179,9 @@ SteamCommunity.prototype._chatPoll = function() {
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") {
@@ -172,7 +190,7 @@ SteamCommunity.prototype._chatPoll = function() {
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") {
@@ -181,29 +199,29 @@ SteamCommunity.prototype._chatPoll = function() {
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);
}
@@ -211,6 +229,9 @@ SteamCommunity.prototype._chatPoll = function() {
}, "steamcommunity");
};
/**
* @private
*/
SteamCommunity.prototype._relogWebChat = function() {
this.emit('debug', "Relogging web chat");
clearTimeout(this._chat.timer);
@@ -218,6 +239,10 @@ SteamCommunity.prototype._relogWebChat = function() {
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
@@ -240,7 +265,7 @@ SteamCommunity.prototype._chatUpdatePersona = function(steamID) {
}, 2000);
return;
}
var persona = {
"steamID": steamID,
"personaName": body.m_strName,
@@ -255,4 +280,4 @@ SteamCommunity.prototype._chatUpdatePersona = function(steamID) {
self.emit('chatPersonaState', steamID, persona);
self.chatFriends[steamID.getSteamID64()] = persona;
}, "steamcommunity");
};
};

View File

@@ -172,7 +172,7 @@ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret,
setTimeout(function() {
// Delete the saved time offset after 12 hours because why not
delete self._timeOffset;
}, 1000 * 60 * 60 * 12);
}, 1000 * 60 * 60 * 12).unref();
});
}

View File

@@ -2,6 +2,7 @@ var SteamCommunity = require('../index.js');
var SteamID = require('steamid');
var xml2js = require('xml2js');
var Cheerio = require('cheerio');
var Helpers = require('./helpers.js');
var EResult = SteamCommunity.EResult;
SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link, addresses, addressIdx) {
@@ -143,7 +144,7 @@ SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback
headline: announcement.title[0],
content: announcement.description[0],
date: new Date(announcement.pubDate[0]),
author: announcement.author[0],
author: (typeof announcement.author === 'undefined') ? null : announcement.author[0],
aid: splitLink[splitLink.length - 1]
}
}).filter(function(announcement) {
@@ -155,22 +156,33 @@ SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback
}, "steamcommunity");
};
SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, callback) {
SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, hidden, callback) {
if(typeof gid === 'string') {
gid = new SteamID(gid);
}
if(typeof hidden === 'function') {
callback = hidden;
hidden = false;
}
var self = this;
var form = {
"sessionID": this.getSessionID(),
"action": "post",
"headline": headline,
"body": content,
"languages[0][headline]": headline,
"languages[0][body]": content
};
if(hidden) {
form.is_hidden = "is_hidden"
}
this.httpRequestPost({
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements",
"form": {
"sessionID": this.getSessionID(),
"action": "post",
"headline": headline,
"body": content,
"languages[0][headline]": headline,
"languages[0][body]": content
}
form
}, function(err, response, body) {
if(!callback) {
return;
@@ -519,6 +531,101 @@ SteamCommunity.prototype.getGroupHistory = function(gid, page, callback) {
}, "steamcommunity");
};
SteamCommunity.prototype.getAllGroupComments = function(gid, from, count, callback) {
if (typeof gid === 'string') {
gid = new SteamID(gid);
}
var options = {
"uri": "https://steamcommunity.com/comment/Clan/render/" + gid.getSteamID64() + "/-1/",
"form": {
"start": from,
"count": count
}
};
var self = this;
this.httpRequestPost(options, function(err, response, body) {
if (err) {
callback(err);
return;
}
var comments = [];
var $ = Cheerio.load(JSON.parse(body).comments_html);
$(".commentthread_comment_content").each(function () {
var comment = {};
var cachedSelector;
var $selector = $(this).find(".commentthread_author_link");
comment.authorName = $($selector).find("bdi").text();
comment.authorId = $($selector).attr("href").replace(/https?:\/\/steamcommunity.com\/(id|profiles)\//, "");
comment.date = Helpers.decodeSteamTime($(this).find(".commentthread_comment_timestamp").text().trim());
$selector = $(this).find(".commentthread_comment_text");
comment.commentId = $($selector).attr("id").replace("comment_content_", "");
comment.text = $($selector).html().trim();
comments.push(comment);
});
callback(null, comments);
}, "steamcommunity");
};
SteamCommunity.prototype.deleteGroupComment = function(gid, cid, callback) {
if (typeof gid === 'string') {
gid = new SteamID(gid);
}
if (typeof cid !== 'string') {
cid = cid.toString();
}
var options = {
"uri": "https://steamcommunity.com/comment/Clan/delete/" + gid.getSteamID64() + "/-1/",
"form": {
"sessionid": this.getSessionID(),
"gidcomment": cid
}
};
var self = this;
this.httpRequestPost(options, function(err, response, body) {
if(!callback) {
return;
}
callback(err || null);
}, "steamcommunity");
};
SteamCommunity.prototype.postGroupComment = function(gid, message, callback) {
if (typeof gid === 'string') {
gid = new SteamID(gid);
}
var options = {
"uri": "https://steamcommunity.com/comment/Clan/post/" + gid.getSteamID64() + "/-1/",
"form": {
"comment": message,
"count": 6,
"sessionid": this.getSessionID()
}
};
var self = this;
this.httpRequestPost(options, function(err, response, body) {
if(!callback) {
return;
}
callback(err || null);
}, "steamcommunity");
};
/**
* Get requests to join a restricted group.
* @param {SteamID|string} gid - The SteamID of the group you want to manage

View File

@@ -1,3 +1,5 @@
const EResult = require('../resources/EResult.js');
exports.isSteamID = function(input) {
var keys = Object.keys(input);
if (keys.length != 4) {
@@ -13,11 +15,42 @@ exports.isSteamID = function(input) {
};
exports.decodeSteamTime = function(time) {
var parts = time.split('@');
if (!parts[0].match(/,/)) {
// no year, assume current year
parts[0] += ", " + (new Date()).getFullYear();
var date = new Date();
if (time.includes("@")) {
var parts = time.split('@');
if (!parts[0].includes(",")) {
// no year, assume current year
parts[0] += ", " + date.getFullYear();
}
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");
if(time.includes("minutes")) {
date.setMinutes(date.getMinutes() - amount);
} else if(time.match(/hour|hours/)) {
date.setHours(date.getHours() - amount);
}
}
return new Date(parts.join('@').replace(/(am|pm)/, ' $1') + " UTC"); // add a space so JS can decode it
return date;
};
/**
* Get an Error object for a particular EResult
* @param {int} eresult
* @returns {null|Error}
*/
exports.eresultError = function(eresult) {
if (eresult == EResult.OK) {
// no error
return null;
}
var err = new Error(EResult[eresult] || ("Error " + eresult));
err.eresult = eresult;
return err;
};

View File

@@ -13,6 +13,11 @@ var Async = require('async');
* 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;

View File

@@ -1,6 +1,10 @@
var SteamCommunity = require('../index.js');
var Cheerio = require('cheerio');
/**
* Get a list of all apps on the market
* @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) {
@@ -11,8 +15,8 @@ SteamCommunity.prototype.getMarketApps = function(callback) {
var $ = Cheerio.load(body);
if ($('.market_search_game_button_group')) {
apps = {};
$('.market_search_game_button_group > a').each(function (i, element) {
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;
@@ -25,3 +29,186 @@ SteamCommunity.prototype.getMarketApps = function(callback) {
}
}, "steamcommunity");
};
/**
* Check if an item is eligible to be turned into gems and if so, get its gem value
* @param {int} appid
* @param {int|string} assetid
* @param {function} callback
*/
SteamCommunity.prototype.getGemValue = function(appid, assetid, callback) {
this._myProfile({
"endpoint": "ajaxgetgoovalue/",
"qs": {
"sessionid": this.getSessionID(),
"appid": appid,
"contextid": 6,
"assetid": assetid
},
"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;
}
if (!body.goo_value || !body.strTitle) {
callback(new Error("Malformed response"));
return;
}
callback(null, {"promptTitle": body.strTitle, "gemValue": parseInt(body.goo_value, 10)});
});
};
/**
* Turn an eligible item into gems.
* @param {int} appid
* @param {int|string} assetid
* @param {int} expectedGemsValue
* @param {function} callback
*/
SteamCommunity.prototype.turnItemIntoGems = function(appid, assetid, expectedGemsValue, callback) {
this._myProfile({
"endpoint": "ajaxgrindintogoo/",
"json": true,
"checkHttpError": false
}, {
"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);
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)});
})
};
/**
* Open a booster pack.
* @param {int} appid
* @param {int|string} assetid
* @param {function} callback
*/
SteamCommunity.prototype.openBoosterPack = function(appid, assetid, callback) {
this._myProfile({
"endpoint": "ajaxunpackbooster/",
"json": true,
"checkHttpError": false
}, {
"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;
}
if (!body.rgItems) {
callback(new Error("Malformed response"));
return;
}
callback(null, body.rgItems);
})
};
/**
* Get details about a gift in your inventory.
* @param {string} giftID
* @param {function} callback
*/
SteamCommunity.prototype.getGiftDetails = function(giftID, callback) {
this.httpRequestPost({
"uri": "https://steamcommunity.com/gifts/" + giftID + "/validateunpack",
"form": {
"sessionid": this.getSessionID()
},
"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;
}
if (!body.packageid || !body.gift_name) {
callback(new Error("Malformed response"));
return;
}
callback(null, {
"giftName": body.gift_name,
"packageID": parseInt(body.packageid, 10),
"owned": body.owned
});
});
};
/**
* Unpack a gift in your inventory to your library.
* @param {string} giftID
* @param {function} callback
*/
SteamCommunity.prototype.redeemGift = function(giftID, callback) {
this.httpRequestPost({
"uri": "https://steamcommunity.com/gifts/" + giftID + "/unpack",
"form": {
"sessionid": this.getSessionID()
},
"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;
}
callback(null);
});
};

View File

@@ -1,7 +1,9 @@
var SteamCommunity = require('../index.js');
var SteamID = require('steamid');
var Cheerio = require('cheerio');
var fs = require('fs');
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,
@@ -10,9 +12,9 @@ SteamCommunity.PrivacyState = {
};
var CommentPrivacyState = {
"1": "commentselfonly",
"2": "commentfriendsonly",
"3": "commentanyone"
"1": 2, // private
"2": 0, // friends only
"3": 1 // anyone
};
SteamCommunity.prototype.setupProfile = function(callback) {
@@ -21,7 +23,7 @@ SteamCommunity.prototype.setupProfile = function(callback) {
if(!callback) {
return;
}
if(err || response.statusCode != 200) {
callback(err || new Error("HTTP error " + response.statusCode));
} else {
@@ -32,182 +34,221 @@ SteamCommunity.prototype.setupProfile = function(callback) {
SteamCommunity.prototype.editProfile = function(settings, callback) {
var self = this;
this._myProfile("edit", null, function(err, response, body) {
if(err || response.statusCode != 200) {
if(callback) {
callback(err || new Error("HTTP error " + response.statusCode));
this._myProfile('edit/info', null, function(err, response, body) {
if (err || response.statusCode != 200) {
if (callback) {
callback(err || new Error('HTTP error ' + response.statusCode));
}
return;
}
var $ = Cheerio.load(body);
var form = $('#editForm');
if(!form) {
if(callback) {
callback(new Error("Malformed response"));
var existingSettings = $('#profile_edit_config').data('profile-edit');
if (!existingSettings || !existingSettings.strPersonaName) {
if (callback) {
callback(new Error('Malformed response'));
}
return;
}
var values = {};
form.serializeArray().forEach(function(item) {
values[item.name] = item.value;
});
for(var i in settings) {
var values = {
sessionID: self.getSessionID(),
type: 'profileSave',
weblink_1_title: '',
weblink_1_url: '',
weblink_2_title: '',
weblink_2_url: '',
weblink_3_title: '',
weblink_3_url: '',
personaName: existingSettings.strPersonaName,
real_name: existingSettings.strRealName,
summary: existingSettings.strSummary,
country: existingSettings.LocationData.locCountryCode,
state: existingSettings.LocationData.locStateCode,
city: existingSettings.LocationData.locCityCode,
customURL: existingSettings.strCustomURL,
json: 1
};
for (var i in settings) {
if(!settings.hasOwnProperty(i)) {
continue;
}
switch(i) {
case 'name':
values.personaName = settings[i];
break;
case 'realName':
values.real_name = settings[i];
break;
case 'summary':
values.summary = settings[i];
break;
case 'country':
values.country = settings[i];
break;
case 'state':
values.state = settings[i];
break;
case 'city':
values.city = settings[i];
break;
case 'customURL':
values.customURL = settings[i];
break;
// These don't work right now
/*
case 'background':
// The assetid of our desired profile background
values.profile_background = settings[i];
break;
case 'featuredBadge':
// Currently, game badges aren't supported
values.favorite_badge_badgeid = settings[i];
break;
case 'primaryGroup':
if(typeof settings[i] === 'object' && settings[i].getSteamID64) {
values.primary_group_steamid = settings[i].getSteamID64();
} else {
values.primary_group_steamid = new SteamID(settings[i]).getSteamID64();
}
break;
*/
// TODO: profile showcases
}
}
self._myProfile("edit", values, function(err, response, body) {
self._myProfile('edit', values, function(err, response, body) {
if (settings.customURL) {
delete self._profileURL;
}
if(err || response.statusCode != 200) {
if(callback) {
callback(err || new Error("HTTP error " + response.statusCode));
}
if (!callback) {
return;
}
// Check for an error
var $ = Cheerio.load(body);
var error = $('#errorText .formRowFields');
if(error) {
error = error.text().trim();
if(error) {
if(callback) {
callback(new Error(error));
}
if (err || response.statusCode != 200) {
callback(err || new Error('HTTP error ' + response.statusCode));
return;
}
try {
var json = JSON.parse(body);
if (!json.success || json.success != 1) {
callback(new Error(json.errmsg || 'Request was not successful'));
return;
}
}
if(callback) {
callback(null);
} catch (ex) {
callback(ex);
}
});
});
};
SteamCommunity.prototype.profileSettings = function(settings, callback) {
var self = this;
this._myProfile("edit/settings", null, function(err, response, body) {
if(err || response.statusCode != 200) {
if(callback) {
callback(err || new Error("HTTP error " + response.statusCode));
this._myProfile('edit/settings', null, (err, response, body) => {
if (err || response.statusCode != 200) {
if (callback) {
callback(err || new Error('HTTP error ' + response.statusCode));
}
return;
}
var $ = Cheerio.load(body);
var form = $('#editForm');
if(!form) {
if(callback) {
callback(new Error("Malformed response"));
var existingSettings = $('#profile_edit_config').data('profile-edit');
if (!existingSettings || !existingSettings.Privacy) {
if (callback) {
callback(new Error('Malformed response'));
}
return;
}
var values = {};
form.serializeArray().forEach(function(item) {
values[item.name] = item.value;
});
for(var i in settings) {
if(!settings.hasOwnProperty(i)) {
// 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;
}
switch(i) {
switch (i) {
case 'profile':
values.privacySetting = settings[i];
privacy.PrivacyProfile = settings[i];
break;
case 'comments':
values.commentSetting = CommentPrivacyState[settings[i]];
commentPermission = CommentPrivacyState[settings[i]];
break;
case 'inventory':
values.inventoryPrivacySetting = settings[i];
privacy.PrivacyInventory = settings[i];
break;
case 'inventoryGifts':
values.inventoryGiftPrivacy = settings[i] ? 1 : 0;
privacy.PrivacyInventoryGifts = settings[i] ? SteamCommunity.PrivacyState.Private : SteamCommunity.PrivacyState.Public;
break;
case 'gameDetails':
privacy.PrivacyOwnedGames = settings[i];
break;
case 'playtime':
privacy.PrivacyPlaytime = settings[i] ? SteamCommunity.PrivacyState.Private : SteamCommunity.PrivacyState.Public;
break;
case 'friendsList':
privacy.PrivacyFriendsList = settings[i];
break;
}
}
self._myProfile("edit/settings", values, function(err, response, body) {
if(err || response.statusCode != 200) {
if(callback) {
callback(err || new Error("HTTP error " + response.statusCode));
this._myProfile({
method: 'POST',
endpoint: 'ajaxsetprivacy/',
json: true,
formData: { // it's multipart because lolvalve
sessionid: this.getSessionID(),
Privacy: JSON.stringify(privacy),
eCommentPermission: commentPermission
}
}, null, function(err, response, body) {
if (err || response.statusCode != 200) {
if (callback) {
callback(err || new Error('HTTP error ' + response.statusCode));
}
return;
}
if(callback) {
callback(null);
if (body.success != 1) {
if (callback) {
callback(new Error(body.success ? 'Error ' + body.success : 'Request was not successful'));
}
return;
}
if (callback) {
callback(null, body.Privacy);
}
});
});
@@ -219,6 +260,12 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) {
format = null;
}
// are we logged in?
if (!this.steamID) {
callback(new Error("Not Logged In"));
return;
}
var self = this;
if(image instanceof Buffer) {
@@ -250,7 +297,7 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) {
}
}
fs.readFile(image, function(err, file) {
FS.readFile(image, function(err, file) {
if(err) {
if(callback) {
callback(err);
@@ -361,3 +408,68 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) {
}, "steamcommunity");
}
};
/**
* Post a new status to your profile activity feed.
* @param {string} statusText - The text of this status update
* @param {{appID: int}} [options] - Options for this status update. All are optional. If you don't pass any options, this can be omitted.
* @param {function} callback - err, postID
*/
SteamCommunity.prototype.postProfileStatus = function(statusText, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
this._myProfile("ajaxpostuserstatus/", {
"appid": options.appID || 0,
"sessionid": this.getSessionID(),
"status_text": statusText
}, (err, res, body) => {
try {
body = JSON.parse(body);
if (body.message) {
callback(new Error(body.message));
return;
}
var match = body.blotter_html.match(/id="userstatus_(\d+)_/);
if (!match) {
callback(new Error("Malformed response"));
return;
}
callback(null, parseInt(match[1], 10));
} catch (ex) {
callback(ex);
}
});
};
/**
* Delete a previously-posted profile status update.
* @param {int} postID
* @param {function} [callback]
*/
SteamCommunity.prototype.deleteProfileStatus = function(postID, callback) {
this._myProfile("ajaxdeleteuserstatus/", {
"sessionid": this.getSessionID(),
"postid": postID
}, (err, res, body) => {
if (!callback) {
return;
}
try {
body = JSON.parse(body);
if (!body.success) {
callback(new Error("Malformed response"));
return;
}
callback(Helpers.eresultError(body.success));
} catch (ex) {
callback(ex);
}
});
};

View File

@@ -1,7 +1,12 @@
var SteamCommunity = require('../index.js');
var SteamID = require('steamid');
var CEconItem = require('../classes/CEconItem.js');
var Helpers = require('./helpers.js');
const Cheerio = require('cheerio');
const Crypto = require('crypto');
const imageSize = require('image-size');
const SteamID = require('steamid');
const SteamCommunity = require('../index.js');
const CEconItem = require('../classes/CEconItem.js');
const Helpers = require('./helpers.js');
SteamCommunity.prototype.addFriend = function(userID, callback) {
if(typeof userID === 'string') {
@@ -217,6 +222,45 @@ SteamCommunity.prototype.getUserAliases = function(userID, callback) {
}, "steamcommunity");
};
/**
* Get the background URL of user's profile.
* @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
* @param {function} callback
*/
SteamCommunity.prototype.getUserProfileBackground = function(userID, callback) {
if (typeof userID === 'string') {
userID = new SteamID(userID);
}
this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64(), (err, response, body) => {
if (err) {
callback(err);
return;
}
var $ = Cheerio.load(body);
var $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(/\(([^)]+)\)/);
if (matcher.length != 2 || !matcher[1].length) {
callback(new Error("Malformed response"));
} else {
callback(null, matcher[1]);
}
} else {
callback(null, null);
}
}, "steamcommunity");
};
SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
if (typeof userID === 'string') {
userID = new SteamID(userID);
@@ -355,17 +399,22 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t
* @param {function} callback
*/
SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) {
if (typeof language === 'function') {
callback = language;
language = "english";
}
if (!userID) {
callback(new Error("The user's SteamID is invalid or missing."));
return;
}
var self = this;
if (typeof userID === 'string') {
userID = new SteamID(userID);
}
if (typeof language === 'function') {
callback = language;
language = "english";
}
var pos = 1;
get([], []);
@@ -389,7 +438,7 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
// We can never get private profile error for our own inventory!
self._notifySessionExpired(err);
}
callback(new Error("This profile is private."));
return;
}
@@ -423,13 +472,13 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
} else {
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);
if (!tradableOnly || (description && description.tradable)) {
body.assets[i].pos = pos++;
(body.assets[i].currencyid ? currency : inventory).push(new CEconItem(body.assets[i], description, contextID));
@@ -457,7 +506,162 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
for (var i = 0; i < descriptions.length; i++) {
quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i];
}
return quickDescriptionLookup[key];
}
};
/**
* Upload an image to Steam and send it to another user over Steam chat.
* @param {SteamID|string} userID - Either a SteamID object or a string that can parse into one
* @param {Buffer} imageContentsBuffer - The image contents, as a Buffer
* @param {{spoiler?: boolean}} [options]
* @param {function} callback
*/
SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, options, callback) {
if (typeof options == 'function') {
callback = options;
options = {};
}
options = options || {};
if (!userID) {
callback(new Error('The user\'s SteamID is invalid or missing'));
return;
}
if (typeof userID == 'string') {
userID = new SteamID(userID);
}
if (!Buffer.isBuffer(imageContentsBuffer)) {
callback(new Error('The image contents must be a Buffer containing an image'));
return;
}
var imageDetails = null;
try {
imageDetails = imageSize(imageContentsBuffer);
} catch (ex) {
callback(ex);
return;
}
var imageHash = Crypto.createHash('sha1');
imageHash.update(imageContentsBuffer);
imageHash = imageHash.digest('hex');
var filename = Date.now() + '_image.' + imageDetails.type;
this.httpRequestPost({
uri: 'https://steamcommunity.com/chat/beginfileupload/?l=english',
headers: {
referer: 'https://steamcommunity.com/chat/'
},
formData: { // it's multipart
sessionid: this.getSessionID(),
l: 'english',
file_size: imageContentsBuffer.length,
file_name: filename,
file_sha: imageHash,
file_image_width: imageDetails.width,
file_image_height: imageDetails.height,
file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type)
},
json: true
}, (err, res, body) => {
if (err) {
if (body && body.success) {
var err2 = Helpers.eresultError(body.success);
if (body.message) {
err2.message = body.message;
}
callback(err2);
} else {
callback(err);
}
return;
}
if (body.success != 1) {
callback(Helpers.eresultError(body.success));
return;
}
var hmac = body.hmac;
var timestamp = body.timestamp;
var startResult = body.result;
if (!startResult || !startResult.ugcid || !startResult.url_host || !startResult.request_headers) {
callback(new Error('Malformed response'));
return;
}
// 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 = {};
startResult.request_headers.forEach((header) => {
headers[header.name.toLowerCase()] = header.value;
});
this.httpRequest({
uri: uploadUrl,
method: 'PUT',
headers,
body: imageContentsBuffer
}, (err, res, body) => {
if (err) {
callback(err);
return;
}
// Now we need to commit the upload
this.httpRequestPost({
uri: 'https://steamcommunity.com/chat/commitfileupload/',
headers: {
referer: 'https://steamcommunity.com/chat/'
},
formData: { // it's multipart again
sessionid: this.getSessionID(),
l: 'english',
file_name: filename,
file_sha: imageHash,
success: '1',
ugcid: startResult.ugcid,
file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type),
file_image_width: imageDetails.width,
file_image_height: imageDetails.height,
timestamp,
hmac,
friend_steamid: userID.getSteamID64(),
spoiler: options.spoiler ? '1' : '0'
},
json: true
}, (err, res, body) => {
if (err) {
callback(err);
return;
}
if (body.success != 1) {
callback(Helpers.eresultError(body.success));
return;
}
if (body.result.success != 1) {
// lol valve
callback(Helpers.eresultError(body.result.success));
return;
}
if (!body.result.details || !body.result.details.url) {
callback(new Error('Malformed response'));
return;
}
callback(null, body.result.details.url);
}, 'steamcommunity');
}, 'steamcommunity');
}, 'steamcommunity');
};

View File

@@ -15,6 +15,10 @@ SteamCommunity.prototype.getWebApiKey = function(domain, callback) {
return callback(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."));
}
var match = body.match(/<p>Key: ([0-9A-F]+)<\/p>/);
if(match) {
// We already have an API key registered
@@ -40,6 +44,10 @@ SteamCommunity.prototype.getWebApiKey = function(domain, callback) {
}, "steamcommunity");
};
/**
* @deprecated No longer works if not logged in via mobile login. Will be removed in a future release.
* @param {function} callback
*/
SteamCommunity.prototype.getWebApiOauthToken = function(callback) {
var self = this;

View File

@@ -1,18 +1,19 @@
var SteamCommunity = require('../index.js');
var ReadLine = require('readline');
var fs = require('fs');
// 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 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("Two-Factor Auth Code: ", function(authCode) {
rl.question("Revocation Code: R", function(rCode) {
doLogin(accountName, password, authCode, "", rCode);
rl.question('Username: ', (accountName) => {
rl.question('Password: ', (password) => {
rl.question('Two-Factor Auth Code: ', (authCode) =>{
rl.question('Revocation Code: R', (rCode) => {
doLogin(accountName, password, authCode, '', rCode);
});
});
});
@@ -20,21 +21,21 @@ rl.question("Username: ", function(accountName) {
function doLogin(accountName, password, authCode, captcha, rCode) {
community.login({
"accountName": accountName,
"password": password,
"twoFactorCode": authCode,
"captcha": captcha
}, function(err, sessionID, cookies, steamguard) {
if(err) {
if(err.message == 'SteamGuard') {
console.log("This account does not have two-factor authentication enabled.");
accountName: accountName,
password: password,
twoFactorCode: authCode,
captcha: captcha
}, (err, sessionID, cookies, steamguard) => {
if (err) {
if (err.message == 'SteamGuard') {
console.log('This account does not have two-factor authentication enabled.');
process.exit();
return;
}
if(err.message == 'CAPTCHA') {
if (err.message == 'CAPTCHA') {
console.log(err.captchaurl);
rl.question("CAPTCHA: ", function(captchaInput) {
rl.question('CAPTCHA: ', (captchaInput) => {
doLogin(accountName, password, authCode, captchaInput);
});
@@ -46,15 +47,15 @@ function doLogin(accountName, password, authCode, captcha, rCode) {
return;
}
console.log("Logged on!");
community.disableTwoFactor("R" + rCode, function(err) {
if(err) {
console.log('Logged on!');
community.disableTwoFactor('R' + rCode, (err) => {
if (err) {
console.log(err);
process.exit();
return;
}
console.log("Two-factor authentication disabled!");
console.log('Two-factor authentication disabled!');
process.exit();
});
});

View File

@@ -1,6 +1,5 @@
var SteamCommunity = require('../index.js');
var ReadLine = require('readline');
var fs = require('fs');
var community = new SteamCommunity();
var rl = ReadLine.createInterface({

View File

@@ -1,45 +1,49 @@
var SteamCommunity = require('../index.js');
var ReadLine = require('readline');
var fs = require('fs');
// 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 ReadLine = require('readline');
const FS = require('fs');
var community = new SteamCommunity();
var rl = ReadLine.createInterface({
"input": process.stdin,
"output": process.stdout
const EResult = SteamCommunity.EResult;
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: ', (accountName) => {
rl.question('Password: ', (password) => {
doLogin(accountName, password);
});
});
function doLogin(accountName, password, authCode, captcha) {
community.login({
"accountName": accountName,
"password": password,
"authCode": authCode,
"captcha": captcha
}, function(err, sessionID, cookies, steamguard) {
if(err) {
if(err.message == 'SteamGuardMobile') {
console.log("This account already has two-factor authentication enabled.");
accountName: accountName,
password: password,
authCode: authCode,
captcha: captcha
}, (err, sessionID, cookies, steamguard) => {
if (err) {
if (err.message == 'SteamGuardMobile') {
console.log('This account already has two-factor authentication enabled.');
process.exit();
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: ', (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: ', (captchaInput) => {
doLogin(accountName, password, authCode, captchaInput);
});
@@ -51,17 +55,17 @@ function doLogin(accountName, password, authCode, captcha) {
return;
}
console.log("Logged on!");
community.enableTwoFactor(function(err, response) {
if(err) {
if(err.eresult == 2) {
console.log("Error: Failed to enable two-factor authentication. Do you have a phone number attached to your account?");
console.log('Logged on!');
community.enableTwoFactor((err, response) => {
if (err) {
if (err.eresult == EResult.Fail) {
console.log('Error: Failed to enable two-factor authentication. Do you have a phone number attached to your account?');
process.exit();
return;
}
if(err.eresult == 84) {
console.log("Error: RateLimitExceeded. Try again later.");
if (err.eresult == EResult.RateLimitExceeded) {
console.log('Error: RateLimitExceeded. Try again later.');
process.exit();
return;
}
@@ -71,15 +75,16 @@ function doLogin(accountName, password, authCode, captcha) {
return;
}
if(response.status != 1) {
console.log("Error: Status " + response.status);
if (response.status != EResult.OK) {
console.log(`Error: Status ${response.status}`);
process.exit();
return;
}
console.log("Writing secrets to twofactor_" + community.steamID.getSteamID64() + ".json");
console.log("Revocation code: " + response.revocation_code);
fs.writeFile("twofactor_" + community.steamID.getSteamID64() + ".json", JSON.stringify(response, null, "\t"));
let filename = `twofactor_${community.steamID.getSteamID64()}.json`;
console.log(`Writing secrets to ${filename}`);
console.log(`Revocation code: ${response.revocation_code}`);
FS.writeFileSync(filename, JSON.stringify(response, null, '\t'));
promptActivationCode(response);
});
@@ -87,10 +92,10 @@ function doLogin(accountName, password, authCode, captcha) {
}
function promptActivationCode(response) {
rl.question("SMS Code: ", function(smsCode) {
community.finalizeTwoFactor(response.shared_secret, smsCode, function(err) {
if(err) {
if(err.message == "Invalid activation code") {
rl.question('SMS Code: ', (smsCode) => {
community.finalizeTwoFactor(response.shared_secret, smsCode, (err) => {
if (err) {
if (err.message == 'Invalid activation code') {
console.log(err);
promptActivationCode(response);
return;
@@ -98,7 +103,7 @@ function promptActivationCode(response) {
console.log(err);
} else {
console.log("Two-factor authentication enabled!");
console.log('Two-factor authentication enabled!');
}
process.exit();

257
index.js
View File

@@ -1,11 +1,11 @@
require('@doctormckay/stats-reporter').setup(require('./package.json'));
const hex2b64 = require('node-bignumber').hex2b64;
const Request = require('request');
const RSA = require('node-bignumber').Key;
const SteamID = require('steamid');
var Request = require('request');
var RSA = require('node-bignumber').Key;
var hex2b64 = require('node-bignumber').hex2b64;
var SteamID = require('steamid');
const Helpers = require('./components/helpers.js');
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36";
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36";
require('util').inherits(SteamCommunity, require('events').EventEmitter);
@@ -15,6 +15,7 @@ SteamCommunity.SteamID = SteamID;
SteamCommunity.ConfirmationType = require('./resources/EConfirmationType.js');
SteamCommunity.EResult = require('./resources/EResult.js');
function SteamCommunity(options) {
options = options || {};
@@ -62,23 +63,30 @@ SteamCommunity.prototype.login = function(details, callback) {
var parts = details.steamguard.split('||');
this._setCookie(Request.cookie('steamMachineAuth' + parts[0] + '=' + encodeURIComponent(parts[1])), true);
}
var disableMobile = details.disableMobile;
var self = this;
// Delete the cache
delete self._profileURL;
// headers required to convince steam that we're logging in from a mobile device so that we can get the oAuth data
var mobileHeaders = {
"X-Requested-With": "com.valvesoftware.android.steam.community",
"Referer": "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client",
"User-Agent": "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Accept": "text/javascript, text/html, application/xml, text/xml, */*"
};
var mobileHeaders = {};
if (!disableMobile) {
mobileHeaders = {
"X-Requested-With": "com.valvesoftware.android.steam.community",
"Referer": "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client",
"User-Agent": this._options.mobileUserAgent || details.mobileUserAgent || "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"Accept": "text/javascript, text/html, application/xml, text/xml, */*"
};
this._setCookie(Request.cookie("mobileClientVersion=0 (2.1.3)"));
this._setCookie(Request.cookie("mobileClient=android"));
} else {
mobileHeaders = {"Referer": "https://steamcommunity.com/login"};
}
this._setCookie(Request.cookie("mobileClientVersion=0 (2.1.3)"));
this._setCookie(Request.cookie("mobileClient=android"));
this.httpRequestPost("https://steamcommunity.com/login/getrsakey/", {
"form": {"username": details.accountName},
"headers": mobileHeaders,
@@ -91,33 +99,39 @@ SteamCommunity.prototype.login = function(details, callback) {
return;
}
if(!body.publickey_mod || !body.publickey_exp) {
if (!body.publickey_mod || !body.publickey_exp) {
deleteMobileCookies();
callback(new Error("Invalid RSA key received"));
return;
}
var key = new RSA();
key.setPublic(body.publickey_mod, body.publickey_exp);
var formObj = {
"captcha_text": details.captcha || "",
"captchagid": self._captchaGid,
"emailauth": details.authCode || "",
"emailsteamid": "",
"password": hex2b64(key.encrypt(details.password)),
"remember_login": "true",
"rsatimestamp": body.timestamp,
"twofactorcode": details.twoFactorCode || "",
"username": details.accountName,
"loginfriendlyname": "",
"donotcache": Date.now()
};
if(!disableMobile){
formObj.oauth_client_id = "DE45CD61";
formObj.oauth_scope = "read_profile write_profile read_client write_client";
formObj.loginfriendlyname = "#login_emailauth_friendlyname_mobile";
}
self.httpRequestPost({
"uri": "https://steamcommunity.com/login/dologin/",
"json": true,
"form": {
"captcha_text": details.captcha || "",
"captchagid": self._captchaGid,
"emailauth": details.authCode || "",
"emailsteamid": "",
"password": hex2b64(key.encrypt(details.password)),
"remember_login": "true",
"rsatimestamp": body.timestamp,
"twofactorcode": details.twoFactorCode || "",
"username": details.accountName,
"oauth_client_id": "DE45CD61",
"oauth_scope": "read_profile write_profile read_client write_client",
"loginfriendlyname": "#login_emailauth_friendlyname_mobile",
"donotcache": Date.now()
},
"form": formObj,
"headers": mobileHeaders
}, function(err, response, body) {
deleteMobileCookies();
@@ -128,38 +142,51 @@ SteamCommunity.prototype.login = function(details, callback) {
}
var error;
if(!body.success && body.emailauth_needed) {
if (!body.success && body.emailauth_needed) {
// Steam Guard (email)
error = new Error("SteamGuard");
error.emaildomain = body.emaildomain;
callback(error);
} else if(!body.success && body.requires_twofactor) {
} else if (!body.success && body.requires_twofactor) {
// Steam Guard (app)
callback(new Error("SteamGuardMobile"));
} else if(!body.success && body.captcha_needed && body.message.match(/Please verify your humanity/)) {
} else if (!body.success && body.captcha_needed && body.message.match(/Please verify your humanity/)) {
error = new Error("CAPTCHA");
error.captchaurl = "https://steamcommunity.com/login/rendercaptcha/?gid=" + body.captcha_gid;
self._captchaGid = body.captcha_gid;
callback(error);
} else if(!body.success) {
} else if (!body.success) {
callback(new Error(body.message || "Unknown error"));
} else if(!body.oauth) {
} else if (!disableMobile && !body.oauth) {
callback(new Error("Malformed response"));
} else {
var sessionID = generateSessionID();
var oAuth = JSON.parse( body.oauth );
var oAuth;
self._setCookie(Request.cookie('sessionid=' + sessionID));
self.steamID = new SteamID(oAuth.steamid);
self.oAuthToken = oAuth.oauth_token;
var cookies = self._jar.getCookieString("https://steamcommunity.com").split(';').map(function(cookie) {
return cookie.trim();
});
if (!disableMobile){
oAuth = JSON.parse(body.oauth);
self.steamID = new SteamID(oAuth.steamid);
self.oAuthToken = oAuth.oauth_token;
}else{
for(var i = 0; i < cookies.length; i++) {
var parts = cookies[i].split('=');
if(parts[0] == 'steamLogin') {
self.steamID = new SteamID(decodeURIComponent(parts[1]).split('||')[0])
break;
}
}
self.oAuthToken = null;
}
// Find the Steam Guard cookie
var steamguard = null;
for(var i = 0; i < cookies.length; i++) {
@@ -171,8 +198,8 @@ SteamCommunity.prototype.login = function(details, callback) {
}
self.setCookies(cookies);
callback(null, sessionID, cookies, steamguard, oAuth.oauth_token);
callback(null, sessionID, cookies, steamguard, disableMobile ? null : oAuth.oauth_token);
}
}, "steamcommunity");
}, "steamcommunity");
@@ -222,6 +249,40 @@ SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) {
}, "steamcommunity");
};
/**
* Get a token that can be used to log onto Steam using steam-user.
* @param {function} callback
*/
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;
}
if (!body.logged_in) {
let e = new Error('Not Logged In');
callback(e);
this._notifySessionExpired(e);
return;
}
if (!body.steamid || !body.account_name || !body.token) {
callback(new Error('Malformed response'));
return;
}
callback(null, {
"steamID": new SteamID(body.steamid),
"accountName": body.account_name,
"webLogonToken": body.token
});
});
};
SteamCommunity.prototype._setCookie = function(cookie, secure) {
var protocol = secure ? "https" : "http";
cookie.secure = !!secure;
@@ -232,14 +293,13 @@ SteamCommunity.prototype._setCookie = function(cookie, secure) {
};
SteamCommunity.prototype.setCookies = function(cookies) {
var self = this;
cookies.forEach(function(cookie) {
cookies.forEach((cookie) => {
var cookieName = cookie.match(/(.+)=/)[1];
if(cookieName == 'steamLogin') {
self.steamID = new SteamID(cookie.match(/=(\d+)/)[1]);
if (cookieName == 'steamLogin' || cookieName == 'steamLoginSecure') {
this.steamID = new SteamID(cookie.match(/=(\d+)/)[1]);
}
self._setCookie(Request.cookie(cookie), !!(cookieName.match(/^steamMachineAuth/) || cookieName.match(/Secure$/)));
this._setCookie(Request.cookie(cookie), !!(cookieName.match(/^steamMachineAuth/) || cookieName.match(/Secure$/)));
});
};
@@ -251,7 +311,7 @@ SteamCommunity.prototype.getSessionID = function() {
return decodeURIComponent(match[2]);
}
}
var sessionID = generateSessionID();
this._setCookie(Request.cookie('sessionid=' + sessionID));
return sessionID;
@@ -273,17 +333,17 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
if(!callback) {
return;
}
if (err) {
callback(err);
return;
}
if (!body || typeof body.success !== 'boolean') {
callback("Invalid response");
return;
}
if (!body.success) {
switch (body.eresult) {
case 15:
@@ -300,7 +360,7 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
return;
}
callback();
}.bind(this), "steamcommunity");
};
@@ -320,7 +380,7 @@ SteamCommunity.prototype.getNotifications = function(callback) {
callback(new Error("Malformed response"));
return;
}
var notifications = {
"trades": body.notifications[1] || 0,
"gameTurns": body.notifications[2] || 0,
@@ -360,16 +420,76 @@ SteamCommunity.prototype.loggedIn = function(callback) {
callback(err || new Error("HTTP error " + response.statusCode));
return;
}
if(response.statusCode == 403) {
callback(null, true, true);
return;
}
callback(null, !!response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^\/]+)\/?/), false);
}, "steamcommunity");
};
SteamCommunity.prototype.getTradeURL = function(callback) {
this._myProfile("tradeoffers/privacy", null, (err, response, body) => {
if (err) {
callback(err);
return;
}
var match = body.match(/https?:\/\/(www.)?steamcommunity.com\/tradeoffer\/new\/?\?partner=\d+(&|&amp;)token=([a-zA-Z0-9-_]+)/);
if (match) {
var token = match[3];
callback(null, match[0], token);
} else {
callback(new Error("Malformed response"));
}
}, "steamcommunity");
};
SteamCommunity.prototype.changeTradeURL = function(callback) {
this._myProfile("tradeoffers/newtradeurl", {"sessionid": this.getSessionID()}, (err, response, body) => {
if (!callback) {
return;
}
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");
};
/**
* Clear your profile name (alias) history.
* @param {function} callback
*/
SteamCommunity.prototype.clearPersonaNameHistory = function(callback) {
this._myProfile("ajaxclearaliashistory/", {"sessionid": this.getSessionID()}, (err, res, body) => {
if (!callback) {
return;
}
if (err) {
return callback(err);
}
if (res.statusCode != 200) {
return callback(new Error("HTTP error " + res.statusCode));
}
try {
body = JSON.parse(body);
callback(Helpers.eresultError(body.success));
} catch (ex) {
return callback(new Error("Malformed response"));
}
});
};
SteamCommunity.prototype._myProfile = function(endpoint, form, callback) {
var self = this;
@@ -391,21 +511,22 @@ SteamCommunity.prototype._myProfile = function(endpoint, form, callback) {
self._profileURL = match[1];
setTimeout(function () {
delete self._profileURL; // delete the cache
}, 60000);
}, 60000).unref();
completeRequest(match[1]);
}, "steamcommunity");
}
function completeRequest(url) {
var options = {
"uri": "https://steamcommunity.com" + url + "/" + endpoint,
"method": "GET"
};
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");
@@ -430,4 +551,4 @@ require('./classes/CSteamUser.js');
/**
@callback SteamCommunity~genericErrorCallback
@param {Error|null} err - An Error object on failure, or null on success
*/
*/

View File

@@ -1,6 +1,6 @@
{
"name": "steamcommunity",
"version": "3.32.3",
"version": "3.41.7",
"description": "Provides an interface for logging into and interacting with the Steam Community website",
"keywords": [
"steam",
@@ -11,19 +11,24 @@
"url": "https://github.com/DoctorMcKay/node-steamcommunity/issues"
},
"license": "MIT",
"author": {
"name": "Alex Corn",
"email": "mckay@doctormckay.com",
"url": "https://www.doctormckay.com"
},
"repository": {
"type": "git",
"url": "https://github.com/DoctorMcKay/node-steamcommunity.git"
},
"dependencies": {
"@doctormckay/stats-reporter": "^1.0.2",
"request": "^2.81.0",
"node-bignumber": "^1.2.1",
"steamid": "^1.0.0",
"xml2js": "^0.4.11",
"async": "^2.6.3",
"cheerio": "0.22.0",
"async": "^2.1.4",
"steam-totp": "^1.3.0"
"image-size": "^0.8.2",
"node-bignumber": "^1.2.1",
"request": "^2.88.0",
"steam-totp": "^1.5.0",
"steamid": "^1.1.3",
"xml2js": "^0.4.22"
},
"engines": {
"node": ">=4.0.0"

View File

@@ -119,6 +119,10 @@ module.exports = {
"GSLTExpired": 106,
"InsufficientFunds": 107,
"TooManyPending": 108,
"NoSiteLicensesFound": 109,
"WGNetworkSendExceeded": 110,
"AccountNotFriends": 111,
"LimitedUserAccount": 112,
// Value-to-name mapping for convenience
"0": "Invalid",
@@ -229,4 +233,8 @@ module.exports = {
"106": "GSLTExpired",
"107": "InsufficientFunds",
"108": "TooManyPending",
"109": "NoSiteLicensesFound",
"110": "WGNetworkSendExceeded",
"111": "AccountNotFriends",
"112": "LimitedUserAccount",
};