mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-19 15:58:39 +00:00
Compare commits
14 Commits
1.0.0.0-pr
...
1.0.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f4ed24704 | ||
|
|
015c6b7bdf | ||
|
|
a17c1fc35a | ||
|
|
19e46ce78d | ||
|
|
cf6ee3b60d | ||
|
|
488003993f | ||
|
|
c64a6fabbc | ||
|
|
eb2751861d | ||
|
|
48a1cf1189 | ||
|
|
98e1d51a48 | ||
|
|
5e22c832a2 | ||
|
|
6523b30f10 | ||
|
|
8fefa7c5af | ||
|
|
b6a5b8942c |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,9 +2,10 @@
|
|||||||
## ArchiSteamFarm
|
## ArchiSteamFarm
|
||||||
#################
|
#################
|
||||||
|
|
||||||
# Ignore all config files, apart from example.xml
|
# Ignore all config files, apart from ones we want to include
|
||||||
ArchiSteamFarm/config/*
|
ArchiSteamFarm/config/*
|
||||||
!ArchiSteamFarm/config/example.xml
|
!ArchiSteamFarm/config/example.xml
|
||||||
|
!ArchiSteamFarm/config/minimal.xml
|
||||||
|
|
||||||
#################
|
#################
|
||||||
## Eclipse
|
## Eclipse
|
||||||
|
|||||||
@@ -123,6 +123,9 @@
|
|||||||
<Content Include="config\example.xml">
|
<Content Include="config\example.xml">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="config\minimal.xml">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\SteamAuth\SteamAuth.csproj">
|
<ProjectReference Include="..\SteamAuth\SteamAuth.csproj">
|
||||||
@@ -139,6 +142,7 @@
|
|||||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' ">if $(ConfigurationName) == Release (
|
<PostBuildEvent Condition=" '$(OS)' != 'Unix' ">if $(ConfigurationName) == Release (
|
||||||
mkdir "$(TargetDir)out" "$(TargetDir)out\config"
|
mkdir "$(TargetDir)out" "$(TargetDir)out\config"
|
||||||
copy "$(TargetDir)config\example.xml" "$(TargetDir)out\config"
|
copy "$(TargetDir)config\example.xml" "$(TargetDir)out\config"
|
||||||
|
copy "$(TargetDir)config\minimal.xml" "$(TargetDir)out\config"
|
||||||
"$(SolutionDir)tools\ILMerge.exe" /out:"$(TargetDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards
|
"$(SolutionDir)tools\ILMerge.exe" /out:"$(TargetDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards
|
||||||
del "$(TargetDir)out\ASF.pdb"
|
del "$(TargetDir)out\ASF.pdb"
|
||||||
)</PostBuildEvent>
|
)</PostBuildEvent>
|
||||||
|
|||||||
@@ -150,6 +150,31 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task<bool?> IsLoggedIn() {
|
||||||
|
if (SteamID == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HtmlDocument htmlDocument = await WebBrowser.UrlGetToHtmlDocument("http://steamcommunity.com/my/profile", SteamCookieDictionary).ConfigureAwait(false);
|
||||||
|
if (htmlDocument == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='account_pulldown']");
|
||||||
|
return htmlNode != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<bool> ReconnectIfNeeded() {
|
||||||
|
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
||||||
|
if (isLoggedIn.HasValue && !isLoggedIn.Value) {
|
||||||
|
Logging.LogGenericInfo(Bot.BotName, "Reconnecting because our sessionID expired!");
|
||||||
|
Bot.SteamClient.Disconnect(); // Bot will handle reconnect
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
internal List<SteamTradeOffer> GetTradeOffers() {
|
internal List<SteamTradeOffer> GetTradeOffers() {
|
||||||
if (ApiKey == null) {
|
if (ApiKey == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
private bool LoggedInElsewhere = false;
|
private bool LoggedInElsewhere = false;
|
||||||
private bool IsRunning = false;
|
private bool IsRunning = false;
|
||||||
private bool IsBeingUsedAsPrimaryAccount = false;
|
|
||||||
private string AuthCode, LoginKey, TwoFactorAuth;
|
private string AuthCode, LoginKey, TwoFactorAuth;
|
||||||
|
|
||||||
internal ArchiHandler ArchiHandler { get; private set; }
|
internal ArchiHandler ArchiHandler { get; private set; }
|
||||||
@@ -70,7 +69,7 @@ namespace ArchiSteamFarm {
|
|||||||
internal bool CardDropsRestricted { get; private set; } = false;
|
internal bool CardDropsRestricted { get; private set; } = false;
|
||||||
internal bool UseAsfAsMobileAuthenticator { get; private set; } = false;
|
internal bool UseAsfAsMobileAuthenticator { get; private set; } = false;
|
||||||
internal bool ShutdownOnFarmingFinished { get; private set; } = false;
|
internal bool ShutdownOnFarmingFinished { get; private set; } = false;
|
||||||
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint> { 303700, 335590, 368020 };
|
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint> { 303700, 335590, 368020, 425280 };
|
||||||
internal bool Statistics { get; private set; } = true;
|
internal bool Statistics { get; private set; } = true;
|
||||||
|
|
||||||
private static bool IsValidCdKey(string key) {
|
private static bool IsValidCdKey(string key) {
|
||||||
@@ -161,15 +160,15 @@ namespace ArchiSteamFarm {
|
|||||||
var fireAndForget = Task.Run(async () => await Start().ConfigureAwait(false));
|
var fireAndForget = Task.Run(async () => await Start().ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AcceptAllConfirmations() {
|
internal async Task AcceptAllConfirmations() {
|
||||||
if (SteamGuardAccount == null) {
|
if (SteamGuardAccount == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SteamGuardAccount.RefreshSession();
|
await SteamGuardAccount.RefreshSessionAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
foreach (Confirmation confirmation in SteamGuardAccount.FetchConfirmations()) {
|
foreach (Confirmation confirmation in await SteamGuardAccount.FetchConfirmationsAsync().ConfigureAwait(false)) {
|
||||||
if (SteamGuardAccount.AcceptConfirmation(confirmation)) {
|
if (SteamGuardAccount.AcceptConfirmation(confirmation)) {
|
||||||
Logging.LogGenericInfo(BotName, "Accepting confirmation: Success!");
|
Logging.LogGenericInfo(BotName, "Accepting confirmation: Success!");
|
||||||
} else {
|
} else {
|
||||||
@@ -542,7 +541,8 @@ namespace ArchiSteamFarm {
|
|||||||
SteamPassword = Program.GetUserInput(BotName, Program.EUserInputType.Password);
|
SteamPassword = Program.GetUserInput(BotName, Program.EUserInputType.Password);
|
||||||
}
|
}
|
||||||
|
|
||||||
SteamUser.LogOnDetails logOnDetails = new SteamUser.LogOnDetails {
|
// TODO: We should use SteamUser.LogOn with proper LoginID once https://github.com/SteamRE/SteamKit/pull/217 gets merged
|
||||||
|
ArchiHandler.HackedLogOn(Program.UniqueID, new SteamUser.LogOnDetails {
|
||||||
Username = SteamLogin,
|
Username = SteamLogin,
|
||||||
Password = SteamPassword,
|
Password = SteamPassword,
|
||||||
AuthCode = AuthCode,
|
AuthCode = AuthCode,
|
||||||
@@ -550,14 +550,7 @@ namespace ArchiSteamFarm {
|
|||||||
TwoFactorCode = TwoFactorAuth,
|
TwoFactorCode = TwoFactorAuth,
|
||||||
SentryFileHash = sentryHash,
|
SentryFileHash = sentryHash,
|
||||||
ShouldRememberPassword = true
|
ShouldRememberPassword = true
|
||||||
};
|
});
|
||||||
|
|
||||||
if (!IsBeingUsedAsPrimaryAccount) {
|
|
||||||
SteamUser.LogOn(logOnDetails);
|
|
||||||
} else {
|
|
||||||
// TODO: We should use SteamUser.LogOn with proper LoginID once https://github.com/SteamRE/SteamKit/pull/217 gets merged
|
|
||||||
ArchiHandler.HackedLogOn(0xBAADF00D, logOnDetails);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnDisconnected(SteamClient.DisconnectedCallback callback) {
|
private async void OnDisconnected(SteamClient.DisconnectedCallback callback) {
|
||||||
@@ -604,13 +597,11 @@ namespace ArchiSteamFarm {
|
|||||||
SteamID steamID = friend.SteamID;
|
SteamID steamID = friend.SteamID;
|
||||||
switch (steamID.AccountType) {
|
switch (steamID.AccountType) {
|
||||||
case EAccountType.Clan:
|
case EAccountType.Clan:
|
||||||
ArchiHandler.DeclineClanInvite(steamID);
|
// TODO: Accept clan invites from master?
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (steamID == SteamMasterID) {
|
if (steamID == SteamMasterID) {
|
||||||
SteamFriends.AddFriend(steamID);
|
SteamFriends.AddFriend(steamID);
|
||||||
} else {
|
|
||||||
SteamFriends.RemoveFriend(steamID);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -712,12 +703,9 @@ namespace ArchiSteamFarm {
|
|||||||
Logging.LogGenericInfo(BotName, "Logged off of Steam: " + callback.Result);
|
Logging.LogGenericInfo(BotName, "Logged off of Steam: " + callback.Result);
|
||||||
|
|
||||||
switch (callback.Result) {
|
switch (callback.Result) {
|
||||||
case EResult.LogonSessionReplaced:
|
|
||||||
Logging.LogGenericInfo(BotName, "This is primary account, changing logic alt -> main");
|
|
||||||
IsBeingUsedAsPrimaryAccount = true;
|
|
||||||
break;
|
|
||||||
case EResult.AlreadyLoggedInElsewhere:
|
case EResult.AlreadyLoggedInElsewhere:
|
||||||
case EResult.LoggedInElsewhere:
|
case EResult.LoggedInElsewhere:
|
||||||
|
case EResult.LogonSessionReplaced:
|
||||||
LoggedInElsewhere = true;
|
LoggedInElsewhere = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -741,15 +729,18 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EResult.InvalidPassword:
|
case EResult.InvalidPassword:
|
||||||
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + result + ", will retry after a longer while");
|
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + result);
|
||||||
await Stop().ConfigureAwait(false);
|
await Stop().ConfigureAwait(false);
|
||||||
|
|
||||||
// InvalidPassword means that we must assume login key has expired
|
// InvalidPassword means usually that login key has expired, if we used it
|
||||||
LoginKey = null;
|
if (!string.IsNullOrEmpty(LoginKey)) {
|
||||||
File.Delete(LoginKeyFile);
|
LoginKey = null;
|
||||||
|
File.Delete(LoginKeyFile);
|
||||||
// InvalidPassword also means that we might get captcha or other network-based throttling
|
Logging.LogGenericInfo(BotName, "Removed expired login key, reconnecting...");
|
||||||
await Utilities.SleepAsync(25 * 60 * 1000).ConfigureAwait(false); // Captcha disappears after around 20 minutes, so we make it 25
|
} else { // If we didn't use login key, InvalidPassword usually means we got captcha or other network-based throttling
|
||||||
|
Logging.LogGenericInfo(BotName, "Will retry after 25 minutes...");
|
||||||
|
await Utilities.SleepAsync(25 * 60 * 1000).ConfigureAwait(false); // Captcha disappears after around 20 minutes, so we make it 25
|
||||||
|
}
|
||||||
|
|
||||||
// After all of that, try again
|
// After all of that, try again
|
||||||
await Start().ConfigureAwait(false);
|
await Start().ConfigureAwait(false);
|
||||||
|
|||||||
@@ -155,10 +155,16 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NowFarming = false;
|
||||||
Logging.LogGenericInfo(Bot.BotName, "Farming is possible!");
|
Logging.LogGenericInfo(Bot.BotName, "Farming is possible!");
|
||||||
|
|
||||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) {
|
||||||
|
Semaphore.Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Logging.LogGenericInfo(Bot.BotName, "Checking badges...");
|
Logging.LogGenericInfo(Bot.BotName, "Checking badges...");
|
||||||
|
|
||||||
// Find the number of badge pages
|
// Find the number of badge pages
|
||||||
@@ -305,7 +311,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
Logging.LogGenericInfo(Bot.BotName, "Sending signal to stop farming");
|
Logging.LogGenericInfo(Bot.BotName, "Sending signal to stop farming");
|
||||||
FarmResetEvent.Set();
|
FarmResetEvent.Set();
|
||||||
while (NowFarming) {
|
for (var i = 0; i < 5 && NowFarming; i++) {
|
||||||
Logging.LogGenericInfo(Bot.BotName, "Waiting for reaction...");
|
Logging.LogGenericInfo(Bot.BotName, "Waiting for reaction...");
|
||||||
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -323,15 +329,22 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool?> ShouldFarm(ulong appID) {
|
private async Task<bool?> ShouldFarm(ulong appID) {
|
||||||
bool? result = null;
|
if (appID == 0) {
|
||||||
HtmlDocument gamePageDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false);
|
return false;
|
||||||
if (gamePageDocument != null) {
|
|
||||||
HtmlNode gamePageNode = gamePageDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
|
||||||
if (gamePageNode != null) {
|
|
||||||
result = !gamePageNode.InnerText.Contains("No card drops");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
|
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false);
|
||||||
|
if (htmlDocument == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||||
|
if (htmlNode == null) {
|
||||||
|
await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !htmlNode.InnerText.Contains("No card drops");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> Farm(uint appID) {
|
private async Task<bool> Farm(uint appID) {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ namespace ArchiSteamFarm {
|
|||||||
private static readonly object ConsoleLock = new object();
|
private static readonly object ConsoleLock = new object();
|
||||||
//private static readonly string ExeName = AssemblyName.Name + ".exe";
|
//private static readonly string ExeName = AssemblyName.Name + ".exe";
|
||||||
|
|
||||||
|
internal static readonly uint UniqueID = (uint) Utilities.Random.Next();
|
||||||
internal static readonly string Version = AssemblyName.Version.ToString();
|
internal static readonly string Version = AssemblyName.Version.ToString();
|
||||||
|
|
||||||
internal static bool ConsoleIsBusy = false;
|
internal static bool ConsoleIsBusy = false;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
Bot.AcceptAllConfirmations();
|
await Bot.AcceptAllConfirmations().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ParseTrade(SteamTradeOffer tradeOffer) {
|
private async Task ParseTrade(SteamTradeOffer tradeOffer) {
|
||||||
@@ -75,20 +75,11 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success, tradeAccepted;
|
|
||||||
|
|
||||||
if (tradeOffer.items_to_give.Count == 0 || tradeOffer.OtherSteamID64 == Bot.SteamMasterID) {
|
if (tradeOffer.items_to_give.Count == 0 || tradeOffer.OtherSteamID64 == Bot.SteamMasterID) {
|
||||||
tradeAccepted = true;
|
|
||||||
Logging.LogGenericInfo(Bot.BotName, "Accepting trade: " + tradeID);
|
Logging.LogGenericInfo(Bot.BotName, "Accepting trade: " + tradeID);
|
||||||
success = await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
|
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
|
||||||
} else {
|
} else {
|
||||||
tradeAccepted = false;
|
Logging.LogGenericInfo(Bot.BotName, "Ignoring trade: " + tradeID);
|
||||||
Logging.LogGenericInfo(Bot.BotName, "Rejecting trade: " + tradeID);
|
|
||||||
success = Bot.ArchiWebHandler.DeclineTradeOffer(tradeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tradeAccepted && success) {
|
|
||||||
// Do whatever we want with success
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,14 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal static class Utilities {
|
internal static class Utilities {
|
||||||
|
internal static readonly Random Random = new Random();
|
||||||
|
|
||||||
internal static async Task SleepAsync(int miliseconds) {
|
internal static async Task SleepAsync(int miliseconds) {
|
||||||
await Task.Delay(miliseconds).ConfigureAwait(false);
|
await Task.Delay(miliseconds).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<!-- Every bot should have it's own unique .xml configuration file, this is example on which you can base on -->
|
<!-- This is full-fledged example config, you may be also interested in minimal.xml for bare minimum one -->
|
||||||
|
<!-- Default values used in config match default ASF values when given config property is not found -->
|
||||||
|
|
||||||
<!-- Notice, if you use special characters reserved for XML, you should escape them -->
|
<!-- Notice, if you use special characters reserved for XML, you should escape them -->
|
||||||
<!-- Escape table: [& - &] | [" - "] | [' - '] | [< - <] | [> - >] -->
|
<!-- Escape table: [& - &] | [" - "] | [' - '] | [< - <] | [> - >] -->
|
||||||
@@ -71,7 +72,7 @@
|
|||||||
<!-- Comma-separated list of IDs that should not be considered for farming -->
|
<!-- Comma-separated list of IDs that should not be considered for farming -->
|
||||||
<!-- Default value includes appIDs that are wrongly appearing on the profile, e.g. Monster Summer Sale -->
|
<!-- Default value includes appIDs that are wrongly appearing on the profile, e.g. Monster Summer Sale -->
|
||||||
<!-- TIP: Most likely you don't want to change it -->
|
<!-- TIP: Most likely you don't want to change it -->
|
||||||
<Blacklist type="HashSet(uint)" value="303700,335590,368020"/>
|
<Blacklist type="HashSet(uint)" value="303700,335590,368020,425280"/>
|
||||||
|
|
||||||
<!-- This switch enables statistics for me - bot will join Archi's SC Farm group and chat -->
|
<!-- This switch enables statistics for me - bot will join Archi's SC Farm group and chat -->
|
||||||
<!-- Consider leaving it at "true", this way I can check how many running bots are active -->
|
<!-- Consider leaving it at "true", this way I can check how many running bots are active -->
|
||||||
|
|||||||
8
ArchiSteamFarm/config/minimal.xml
Normal file
8
ArchiSteamFarm/config/minimal.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<!-- This is minimalistic config to "just make ASF work" -->
|
||||||
|
<!-- For full-fledged config, please take a look at example.xml -->
|
||||||
|
<Enabled type="bool" value="false"/>
|
||||||
|
<SteamLogin type="string" value="null"/>
|
||||||
|
<SteamPassword type="string" value="null"/>
|
||||||
|
</configuration>
|
||||||
@@ -6,6 +6,7 @@ using System.Net;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace SteamAuth
|
namespace SteamAuth
|
||||||
{
|
{
|
||||||
@@ -171,6 +172,56 @@ namespace SteamAuth
|
|||||||
return ret.ToArray();
|
return ret.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Confirmation[]> FetchConfirmationsAsync()
|
||||||
|
{
|
||||||
|
string url = this.GenerateConfirmationURL();
|
||||||
|
|
||||||
|
CookieContainer cookies = new CookieContainer();
|
||||||
|
this.Session.AddCookies(cookies);
|
||||||
|
|
||||||
|
string response = await SteamWeb.RequestAsync(url, "GET", null, cookies);
|
||||||
|
|
||||||
|
/*So you're going to see this abomination and you're going to be upset.
|
||||||
|
It's understandable. But the thing is, regex for HTML -- while awful -- makes this way faster than parsing a DOM, plus we don't need another library.
|
||||||
|
And because the data is always in the same place and same format... It's not as if we're trying to naturally understand HTML here. Just extract strings.
|
||||||
|
I'm sorry. */
|
||||||
|
|
||||||
|
Regex confIDRegex = new Regex("data-confid=\"(\\d+)\"");
|
||||||
|
Regex confKeyRegex = new Regex("data-key=\"(\\d+)\"");
|
||||||
|
Regex confDescRegex = new Regex("<div>((Confirm|Trade with|Sell -) .+)</div>");
|
||||||
|
|
||||||
|
if (response == null || !(confIDRegex.IsMatch(response) && confKeyRegex.IsMatch(response) && confDescRegex.IsMatch(response)))
|
||||||
|
{
|
||||||
|
if (response == null || !response.Contains("<div>Nothing to confirm</div>"))
|
||||||
|
{
|
||||||
|
throw new WGTokenInvalidException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Confirmation[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchCollection confIDs = confIDRegex.Matches(response);
|
||||||
|
MatchCollection confKeys = confKeyRegex.Matches(response);
|
||||||
|
MatchCollection confDescs = confDescRegex.Matches(response);
|
||||||
|
|
||||||
|
List<Confirmation> ret = new List<Confirmation>();
|
||||||
|
for (int i = 0; i < confIDs.Count; i++)
|
||||||
|
{
|
||||||
|
string confID = confIDs[i].Groups[1].Value;
|
||||||
|
string confKey = confKeys[i].Groups[1].Value;
|
||||||
|
string confDesc = confDescs[i].Groups[1].Value;
|
||||||
|
Confirmation conf = new Confirmation()
|
||||||
|
{
|
||||||
|
ConfirmationDescription = confDesc,
|
||||||
|
ConfirmationID = confID,
|
||||||
|
ConfirmationKey = confKey
|
||||||
|
};
|
||||||
|
ret.Add(conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public bool AcceptConfirmation(Confirmation conf)
|
public bool AcceptConfirmation(Confirmation conf)
|
||||||
{
|
{
|
||||||
return _sendConfirmationAjax(conf, "allow");
|
return _sendConfirmationAjax(conf, "allow");
|
||||||
@@ -213,6 +264,38 @@ namespace SteamAuth
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Refreshes the Steam session. Necessary to perform confirmations if your session has expired or changed.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> RefreshSessionAsync()
|
||||||
|
{
|
||||||
|
string url = APIEndpoints.MOBILEAUTH_GETWGTOKEN;
|
||||||
|
NameValueCollection postData = new NameValueCollection();
|
||||||
|
postData.Add("access_token", this.Session.OAuthToken);
|
||||||
|
|
||||||
|
string response = await SteamWeb.RequestAsync(url, "POST", postData);
|
||||||
|
if (response == null) return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var refreshResponse = JsonConvert.DeserializeObject<RefreshSessionDataResponse>(response);
|
||||||
|
if (refreshResponse == null || refreshResponse.Response == null || String.IsNullOrEmpty(refreshResponse.Response.Token))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string token = this.Session.SteamID + "%7C%7C" + refreshResponse.Response.Token;
|
||||||
|
string tokenSecure = this.Session.SteamID + "%7C%7C" + refreshResponse.Response.TokenSecure;
|
||||||
|
|
||||||
|
this.Session.SteamLogin = token;
|
||||||
|
this.Session.SteamLoginSecure = tokenSecure;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool _sendConfirmationAjax(Confirmation conf, string op)
|
private bool _sendConfirmationAjax(Confirmation conf, string op)
|
||||||
{
|
{
|
||||||
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/ajaxop";
|
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/ajaxop";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace SteamAuth
|
namespace SteamAuth
|
||||||
{
|
{
|
||||||
@@ -67,12 +68,67 @@ namespace SteamAuth
|
|||||||
using (StreamReader responseStream = new StreamReader(response.GetResponseStream()))
|
using (StreamReader responseStream = new StreamReader(response.GetResponseStream()))
|
||||||
{
|
{
|
||||||
string responseData = responseStream.ReadToEnd();
|
string responseData = responseStream.ReadToEnd();
|
||||||
|
|
||||||
return responseData;
|
return responseData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (WebException ex)
|
catch (WebException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string> RequestAsync(string url, string method, NameValueCollection data = null, CookieContainer cookies = null, NameValueCollection headers = null, string referer = APIEndpoints.COMMUNITY_BASE)
|
||||||
|
{
|
||||||
|
string query = (data == null ? string.Empty : string.Join("&", Array.ConvertAll(data.AllKeys, key => String.Format("{0}={1}", WebUtility.UrlEncode(key), WebUtility.UrlEncode(data[key])))));
|
||||||
|
if (method == "GET")
|
||||||
|
{
|
||||||
|
url += (url.Contains("?") ? "&" : "?") + query;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||||
|
request.Method = method;
|
||||||
|
request.Accept = "text/javascript, text/html, application/xml, text/xml, */*";
|
||||||
|
request.UserAgent = "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";
|
||||||
|
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
|
||||||
|
request.Referer = referer;
|
||||||
|
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
request.Headers.Add(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cookies != null)
|
||||||
|
{
|
||||||
|
request.CookieContainer = cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == "POST")
|
||||||
|
{
|
||||||
|
request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
|
||||||
|
request.ContentLength = query.Length;
|
||||||
|
|
||||||
|
StreamWriter requestStream = new StreamWriter(request.GetRequestStream());
|
||||||
|
requestStream.Write(query);
|
||||||
|
requestStream.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HttpWebResponse response = (HttpWebResponse) await request.GetResponseAsync();
|
||||||
|
|
||||||
|
if (response.StatusCode != HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (StreamReader responseStream = new StreamReader(response.GetResponseStream()))
|
||||||
|
{
|
||||||
|
string responseData = responseStream.ReadToEnd();
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (WebException)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Net;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Net;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace SteamAuth
|
namespace SteamAuth
|
||||||
@@ -21,6 +23,15 @@ namespace SteamAuth
|
|||||||
return Util.GetSystemUnixTime() + _timeDifference;
|
return Util.GetSystemUnixTime() + _timeDifference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<long> GetSteamTimeAsync()
|
||||||
|
{
|
||||||
|
if (!TimeAligner._aligned)
|
||||||
|
{
|
||||||
|
await TimeAligner.AlignTimeAsync();
|
||||||
|
}
|
||||||
|
return Util.GetSystemUnixTime() + _timeDifference;
|
||||||
|
}
|
||||||
|
|
||||||
public static void AlignTime()
|
public static void AlignTime()
|
||||||
{
|
{
|
||||||
long currentTime = Util.GetSystemUnixTime();
|
long currentTime = Util.GetSystemUnixTime();
|
||||||
@@ -33,13 +44,30 @@ namespace SteamAuth
|
|||||||
TimeAligner._timeDifference = (int)(query.Response.ServerTime - currentTime);
|
TimeAligner._timeDifference = (int)(query.Response.ServerTime - currentTime);
|
||||||
TimeAligner._aligned = true;
|
TimeAligner._aligned = true;
|
||||||
}
|
}
|
||||||
catch (WebException e)
|
catch (WebException)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task AlignTimeAsync()
|
||||||
|
{
|
||||||
|
long currentTime = Util.GetSystemUnixTime();
|
||||||
|
WebClient client = new WebClient();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string response = await client.UploadStringTaskAsync(new Uri(APIEndpoints.TWO_FACTOR_TIME_QUERY), "steamid=0");
|
||||||
|
TimeQuery query = JsonConvert.DeserializeObject<TimeQuery>(response);
|
||||||
|
TimeAligner._timeDifference = (int)(query.Response.ServerTime - currentTime);
|
||||||
|
TimeAligner._aligned = true;
|
||||||
|
}
|
||||||
|
catch (WebException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class TimeQuery
|
internal class TimeQuery
|
||||||
{
|
{
|
||||||
[JsonProperty("response")]
|
[JsonProperty("response")]
|
||||||
|
|||||||
Reference in New Issue
Block a user