Compare commits

...

30 Commits

Author SHA1 Message Date
JustArchi
e508e99d14 Be smarter ASF 2016-06-19 07:37:31 +02:00
JustArchi
fdc705e955 Further enhance new 2FA, closes #161 2016-06-19 07:25:02 +02:00
JustArchi
38f48841bd Time to break things, closes #169 2016-06-19 05:57:29 +02:00
JustArchi
2ebce59ee7 WIP: Gigantic work on #252
TODO: Market confirmations, cleanup, code review, shitload of tests...
2016-06-19 05:40:46 +02:00
JustArchi
adefa6446d Fix SteamAuth memory leak
I'll need to rewrite the entire module sooner or later anyway...
2016-06-18 22:45:44 +02:00
JustArchi
f18c3b301e Uh 2016-06-15 03:39:16 +02:00
JustArchi
b79265e74a Misc 2016-06-15 03:38:51 +02:00
JustArchi
50853d8d7e Add license badge 2016-06-15 03:36:50 +02:00
JustArchi
7e084bf50b Code review 2016-06-13 18:08:19 +02:00
JustArchi
60a02a4c6a Misc 2016-06-12 23:01:17 +02:00
JustArchi
fcfdbdd220 Bump 2016-06-12 03:06:31 +02:00
JustArchi
ac10f32431 Forward ArchiBoT logic of trade holds for steam sale cards 2016-06-12 02:18:18 +02:00
JustArchi
5d7e0290d7 Bump 2016-06-11 03:42:53 +02:00
JustArchi
f170f16919 Catch SendAsync() exception when debugging 2016-06-11 03:39:25 +02:00
JustArchi
96d9ea6056 Bump 2016-06-11 02:02:01 +02:00
JustArchi
d8bf424ac3 Make STM accept trading cards only, #245 2016-06-11 00:45:07 +02:00
JustArchi
95d2860afd Add AppVeyor/Travis notifications to ASF chat 2016-06-10 21:45:51 +02:00
JustArchi
a75ed7047b Misc 2016-06-10 21:11:18 +02:00
JustArchi
d627570cc9 Bump 2016-06-10 21:10:43 +02:00
JustArchi
4e22d7fcd1 Disgusting fix for broken Mono 2016-06-10 20:51:10 +02:00
JustArchi
bb05f4c67a Misc 2016-06-10 17:40:37 +02:00
JustArchi
238838b0fd Misc 2016-06-10 17:06:20 +02:00
JustArchi
ae8413b72b Add chat link 2016-06-10 16:48:46 +02:00
JustArchi
027e301420 Turns out to be mono bug, hooray! 2016-06-10 01:27:07 +02:00
JustArchi
1a6c5a3cff Fix trades with non-steam items 2016-06-10 01:16:05 +02:00
JustArchi
378a87bc86 Fix broken tabs 2016-06-10 00:49:44 +02:00
JustArchi
27635d260b Code review + some extra debug 2016-06-10 00:47:38 +02:00
JustArchi
8f99620598 Bump 2016-06-09 23:53:21 +02:00
JustArchi
bb285512d1 Update ILRepack to another self-compiled version, #243 2016-06-09 23:49:01 +02:00
JustArchi
88690d8c09 Bump 2016-06-09 19:20:06 +02:00
49 changed files with 1025 additions and 1862 deletions

View File

@@ -10,3 +10,9 @@ mono:
notifications:
email: false
webhooks:
urls:
- https://webhooks.gitter.im/e/df82484f12510c3f2516
on_success: always # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: never # options: [always|never|change] default: always

View File

@@ -1,12 +1,10 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchiSteamFarm", "ArchiSteamFarm\ArchiSteamFarm.csproj", "{35AF7887-08B9-40E8-A5EA-797D8B60B30C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamAuth", "SteamAuth\SteamAuth.csproj", "{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigGenerator", "ConfigGenerator\ConfigGenerator.csproj", "{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}"
ProjectSection(ProjectDependencies) = postProject
{35AF7887-08B9-40E8-A5EA-797D8B60B30C} = {35AF7887-08B9-40E8-A5EA-797D8B60B30C}
@@ -27,10 +25,6 @@ Global
{35AF7887-08B9-40E8-A5EA-797D8B60B30C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35AF7887-08B9-40E8-A5EA-797D8B60B30C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35AF7887-08B9-40E8-A5EA-797D8B60B30C}.Release|Any CPU.Build.0 = Release|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.Build.0 = Release|Any CPU
{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}.Release|Any CPU.ActiveCfg = Release|Any CPU

View File

@@ -3,6 +3,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ASF/@EntryIndexedValue">ASF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FA/@EntryIndexedValue">FA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FS/@EntryIndexedValue">FS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HTML/@EntryIndexedValue">HTML</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OK/@EntryIndexedValue">OK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PIN/@EntryIndexedValue">PIN</s:String>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
</configuration>

View File

@@ -59,7 +59,7 @@ namespace ArchiSteamFarm {
Unknown = 0,
Trading = 1,
// Only custom below, different than ones available as user_notification_type
Items = 255
Items = 254
}
internal readonly HashSet<ENotification> Notifications;
@@ -71,6 +71,10 @@ namespace ArchiSteamFarm {
JobID = jobID;
if (msg.notifications.Count == 0) {
return;
}
Notifications = new HashSet<ENotification>();
foreach (CMsgClientUserNotifications.Notification notification in msg.notifications) {
Notifications.Add((ENotification) notification.user_notification_type);

View File

@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ArchiSteamFarm</RootNamespace>
<AssemblyName>ArchiSteamFarm</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<IsWebBootstrapper>false</IsWebBootstrapper>
<TargetFrameworkProfile />
@@ -109,6 +109,9 @@
<Compile Include="JSON\GitHub.cs" />
<Compile Include="JSON\Steam.cs" />
<Compile Include="Logging.cs" />
<Compile Include="MobileAuthenticator.cs" />
<Compile Include="Mono.cs" />
<Compile Include="ObsoleteSteamGuardAccount.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Trading.cs" />
@@ -144,12 +147,6 @@
<ItemGroup>
<Content Include="cirno.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SteamAuth\SteamAuth.csproj">
<Project>{5ad0934e-f6c4-4ae5-83af-c788313b2a87}</Project>
<Name>SteamAuth</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View File

@@ -47,6 +47,7 @@ namespace ArchiSteamFarm {
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
private readonly WebBrowser WebBrowser;
private ulong SteamID;
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
internal static void Init() {
@@ -62,7 +63,6 @@ namespace ArchiSteamFarm {
int index = hashName.IndexOf('-');
if (index < 1) {
Logging.LogNullError(nameof(index));
return 0;
}
@@ -123,12 +123,9 @@ namespace ArchiSteamFarm {
return false;
}
ulong steamID = steamClient.SteamID;
if (steamID == 0) {
return false;
}
SteamID = steamClient.SteamID;
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString()));
// Generate an AES session key
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
@@ -155,7 +152,7 @@ namespace ArchiSteamFarm {
try {
authResult = iSteamUserAuth.AuthenticateUser(
steamid: steamID,
steamid: SteamID,
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
method: WebRequestMethods.Http.Post,
@@ -174,6 +171,7 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Success!", Bot.BotName);
WebBrowser.CookieContainer.Add(new Cookie("steamid", SteamID.ToString(), "/", "." + SteamCommunityHost)); // TODO: Check if needed for mobile auth
WebBrowser.CookieContainer.Add(new Cookie("sessionid", sessionID, "/", "." + SteamCommunityHost));
string steamLogin = authResult["token"].Value;
@@ -239,6 +237,80 @@ namespace ArchiSteamFarm {
return await WebBrowser.UrlPostRetry(request, data).ConfigureAwait(false);
}
internal async Task<HtmlDocument> GetConfirmations(string deviceID, string confirmationHash, uint time) {
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0)) {
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time));
return null;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
}
string request = SteamCommunityURL + "/mobileconf/conf?p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf";
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
}
internal async Task<Steam.ConfirmationDetails> GetConfirmationDetails(string deviceID, string confirmationHash, uint time, uint confirmationID) {
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmationID == 0)) {
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmationID), Bot.BotName);
return null;
}
string request = SteamCommunityURL + "/mobileconf/details/" + confirmationID + "?p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf";
string json = await WebBrowser.UrlGetToContentRetry(request).ConfigureAwait(false);
if (string.IsNullOrEmpty(json)) {
return null;
}
Steam.ConfirmationDetails response;
try {
response = JsonConvert.DeserializeObject<Steam.ConfirmationDetails>(json);
} catch (JsonException e) {
Logging.LogGenericException(e, Bot.BotName);
return null;
}
if (response != null) {
return response;
}
Logging.LogNullError(nameof(response), Bot.BotName);
return null;
}
internal async Task<bool> HandleConfirmation(string deviceID, string confirmationHash, uint time, uint confirmationID, ulong confirmationKey, bool accept) {
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmationID == 0) || (confirmationKey == 0)) {
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmationID) + " || " + nameof(confirmationKey), Bot.BotName);
return false;
}
string request = SteamCommunityURL + "/mobileconf/ajaxop?op=" + (accept ? "allow" : "cancel") + "&p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf&cid=" + confirmationID + "&ck=" + confirmationKey;
string json = await WebBrowser.UrlGetToContentRetry(request).ConfigureAwait(false);
if (string.IsNullOrEmpty(json)) {
return false;
}
Steam.ConfirmationResponse response;
try {
response = JsonConvert.DeserializeObject<Steam.ConfirmationResponse>(json);
} catch (JsonException e) {
Logging.LogGenericException(e, Bot.BotName);
return false;
}
if (response != null) {
return response.Success;
}
Logging.LogNullError(nameof(response), Bot.BotName);
return false;
}
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
@@ -284,9 +356,7 @@ namespace ArchiSteamFarm {
internal Dictionary<uint, string> GetOwnedGames(ulong steamID) {
if ((steamID == 0) || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
// TODO: Correct this when Mono 4.4+ will be a latest stable one | https://bugzilla.xamarin.com/show_bug.cgi?id=39455
Logging.LogNullError("steamID || SteamApiKey", Bot.BotName);
//Logging.LogNullError(nameof(steamID) + " || " + nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
Logging.LogNullError(nameof(steamID) + " || " + nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
return null;
}
@@ -326,11 +396,85 @@ namespace ArchiSteamFarm {
return result;
}
internal uint GetServerTime() {
KeyValue response = null;
using (dynamic iTwoFactorService = WebAPI.GetInterface("ITwoFactorService", Bot.BotConfig.SteamApiKey)) {
iTwoFactorService.Timeout = Timeout;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
try {
response = iTwoFactorService.QueryTime(
method: WebRequestMethods.Http.Post,
secure: !Program.GlobalConfig.ForceHttp
);
} catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName);
}
}
}
if (response != null) {
return (uint) response["server_time"].AsUnsignedLong();
}
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return 0;
}
internal async Task<byte?> GetTradeHoldDuration(ulong tradeID) {
if (tradeID == 0) {
Logging.LogNullError(nameof(tradeID), Bot.BotName);
return null;
}
string request = SteamCommunityURL + "/tradeoffer/" + tradeID;
HtmlDocument htmlDocument = await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
if (htmlDocument == null) {
return null;
}
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@class='pagecontent']/script");
if (htmlNode == null) {
Logging.LogNullError(nameof(htmlNode), Bot.BotName);
return null;
}
string text = htmlNode.InnerText;
if (string.IsNullOrEmpty(text)) {
Logging.LogNullError(nameof(text), Bot.BotName);
return null;
}
int index = text.IndexOf("g_daysTheirEscrow = ", StringComparison.Ordinal);
if (index < 0) {
Logging.LogNullError(nameof(index), Bot.BotName);
return null;
}
index += 20;
text = text.Substring(index);
index = text.IndexOf(';');
if (index < 0) {
Logging.LogNullError(nameof(index), Bot.BotName);
return null;
}
text = text.Substring(0, index);
byte holdDuration;
if (byte.TryParse(text, out holdDuration)) {
return holdDuration;
}
Logging.LogNullError(nameof(holdDuration), Bot.BotName);
return null;
}
internal HashSet<Steam.TradeOffer> GetTradeOffers() {
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
// TODO: Correct this when Mono 4.4+ will be a latest stable one | https://bugzilla.xamarin.com/show_bug.cgi?id=39455
Logging.LogNullError("SteamApiKey", Bot.BotName);
//Logging.LogNullError(nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
Logging.LogNullError(nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
return null;
}
@@ -370,13 +514,18 @@ namespace ArchiSteamFarm {
}
uint appID = 0;
Steam.Item.EType type = Steam.Item.EType.Unknown;
string hashName = description["market_hash_name"].Value;
if (!string.IsNullOrEmpty(hashName)) {
appID = GetAppIDFromMarketHashName(hashName);
}
if (appID == 0) {
appID = (uint) description["appid"].AsUnsignedLong();
}
Steam.Item.EType type = Steam.Item.EType.Unknown;
string descriptionType = description["type"].Value;
if (!string.IsNullOrEmpty(descriptionType)) {
type = GetItemType(descriptionType);
@@ -462,9 +611,7 @@ namespace ArchiSteamFarm {
internal bool DeclineTradeOffer(ulong tradeID) {
if ((tradeID == 0) || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
// TODO: Correct this when Mono 4.4+ will be a latest stable one | https://bugzilla.xamarin.com/show_bug.cgi?id=39455
Logging.LogNullError("tradeID || SteamApiKey", Bot.BotName);
//Logging.LogNullError(nameof(tradeID) + " || " + nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
Logging.LogNullError(nameof(tradeID) + " || " + nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
return false;
}
@@ -539,6 +686,19 @@ namespace ArchiSteamFarm {
appID = GetAppIDFromMarketHashName(hashName);
}
if (appID == 0) {
string appIDString = description["appid"].ToString();
if (string.IsNullOrEmpty(appIDString)) {
Logging.LogNullError(nameof(appIDString), Bot.BotName);
continue;
}
if (!uint.TryParse(appIDString, out appID)) {
Logging.LogNullError(nameof(appID), Bot.BotName);
continue;
}
}
Steam.Item.EType type = Steam.Item.EType.Unknown;
string descriptionType = description["type"].ToString();

View File

@@ -23,7 +23,6 @@
*/
using Newtonsoft.Json;
using SteamAuth;
using SteamKit2;
using SteamKit2.Internal;
using System;
@@ -61,8 +60,7 @@ namespace ArchiSteamFarm {
private readonly SteamApps SteamApps;
private readonly SteamFriends SteamFriends;
private readonly SteamUser SteamUser;
private readonly Timer AcceptConfirmationsTimer;
private readonly Timer SendItemsTimer;
private readonly Timer AcceptConfirmationsTimer, SendItemsTimer;
private readonly Trading Trading;
internal bool KeepRunning { get; private set; }
@@ -133,18 +131,16 @@ namespace ArchiSteamFarm {
}
if (!BotConfig.Enabled) {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
Logging.LogGenericInfo("Not initializing this instance because it's disabled in config file", botName);
return;
}
lock (Bots) {
if (Bots.ContainsKey(botName)) {
return;
}
Bots[botName] = this;
if (Bots.ContainsKey(botName)) {
throw new Exception("That bot is already defined!");
}
Bots[botName] = this;
SentryFile = botPath + ".bin";
BotDatabase = BotDatabase.Load(botPath + ".db");
@@ -153,7 +149,18 @@ namespace ArchiSteamFarm {
return;
}
if (BotDatabase.SteamGuardAccount == null) {
// TODO: Converter code will be removed soon
if (BotDatabase.SteamGuardAccount != null) {
Logging.LogGenericWarning("Converting old ASF 2FA V2.0 format into new ASF 2FA V2.1 format...");
BotDatabase.MobileAuthenticator = MobileAuthenticator.LoadFromSteamGuardAccount(BotDatabase.SteamGuardAccount);
Logging.LogGenericInfo("Done! If you didn't make a copy of your revocation code yet, then it's a good moment to do so: " + BotDatabase.SteamGuardAccount.RevocationCode);
Logging.LogGenericWarning("ASF will not keep this code anymore!");
BotDatabase.SteamGuardAccount = null;
}
if (BotDatabase.MobileAuthenticator != null) {
BotDatabase.MobileAuthenticator.Init(this);
} else {
// Support and convert SDA files
string maFilePath = botPath + ".maFile";
if (File.Exists(maFilePath)) {
@@ -234,57 +241,54 @@ namespace ArchiSteamFarm {
Start().Forget();
}
internal async Task<bool> AcceptConfirmations(bool confirm, Confirmation.ConfirmationType allowedConfirmationType = Confirmation.ConfirmationType.Unknown) {
if (BotDatabase.SteamGuardAccount == null) {
return true;
internal async Task AcceptConfirmations(bool accept, Steam.ConfirmationDetails.EType acceptedType = Steam.ConfirmationDetails.EType.Unknown, ulong acceptedSteamID = 0, HashSet<ulong> acceptedTradeIDs = null) {
if (BotDatabase.MobileAuthenticator == null) {
return;
}
bool result = false;
for (byte i = 0; (i < WebBrowser.MaxRetries) && !result; i++) {
result = true;
HashSet<MobileAuthenticator.Confirmation> confirmations = await BotDatabase.MobileAuthenticator.GetConfirmations().ConfigureAwait(false);
if ((confirmations == null) || (confirmations.Count == 0)) {
return;
}
try {
if (!await BotDatabase.SteamGuardAccount.RefreshSessionAsync().ConfigureAwait(false)) {
result = false;
continue;
if (acceptedType != Steam.ConfirmationDetails.EType.Unknown) {
if (confirmations.RemoveWhere(confirmation => confirmation.Type != acceptedType) > 0) {
confirmations.TrimExcess();
if (confirmations.Count == 0) {
return;
}
Confirmation[] confirmations = await BotDatabase.SteamGuardAccount.FetchConfirmationsAsync().ConfigureAwait(false);
if (confirmations == null) {
return true;
}
foreach (Confirmation confirmation in confirmations.Where(confirmation => (allowedConfirmationType == Confirmation.ConfirmationType.Unknown) || (confirmation.ConfType == allowedConfirmationType))) {
if (confirm) {
if (BotDatabase.SteamGuardAccount.AcceptConfirmation(confirmation)) {
continue;
}
result = false;
break;
}
if (BotDatabase.SteamGuardAccount.DenyConfirmation(confirmation)) {
continue;
}
result = false;
break;
}
} catch (SteamGuardAccount.WGTokenInvalidException) {
result = false;
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
return false;
}
}
if (result) {
return true;
if ((acceptedSteamID != 0) || ((acceptedTradeIDs != null) && (acceptedTradeIDs.Count > 0))) {
HashSet<MobileAuthenticator.Confirmation> ignoredConfirmations = new HashSet<MobileAuthenticator.Confirmation>();
// TODO: This could be potentially multi-threaded like below
foreach (MobileAuthenticator.Confirmation confirmation in confirmations) {
Steam.ConfirmationDetails details = await BotDatabase.MobileAuthenticator.GetConfirmationDetails(confirmation).ConfigureAwait(false);
if (details == null) {
ignoredConfirmations.Add(confirmation);
continue;
}
if ((acceptedSteamID != 0) && (acceptedSteamID != details.OtherSteamID64)) {
ignoredConfirmations.Add(confirmation);
continue;
}
if ((acceptedTradeIDs != null) && !acceptedTradeIDs.Contains(details.TradeOfferID)) {
ignoredConfirmations.Add(confirmation);
}
}
confirmations.ExceptWith(ignoredConfirmations);
confirmations.TrimExcess();
if (confirmations.Count == 0) {
return;
}
}
Logging.LogGenericWTF("Could not accept confirmations even after " + WebBrowser.MaxRetries + " tries", BotName);
return false;
await confirmations.ForEachAsync(async confirmation => await BotDatabase.MobileAuthenticator.HandleConfirmation(confirmation, accept).ConfigureAwait(false)).ConfigureAwait(false);
}
internal async Task<bool> RefreshSession() {
@@ -302,7 +306,7 @@ namespace ArchiSteamFarm {
return false;
}
if ((callback == null) || (callback.Result != EResult.OK) || string.IsNullOrEmpty(callback.Nonce)) {
if ((callback == null) || string.IsNullOrEmpty(callback.Nonce)) {
Start().Forget();
return false;
}
@@ -347,11 +351,9 @@ namespace ArchiSteamFarm {
if (message.IndexOf(' ') < 0) {
switch (message) {
case "!2fa":
return Response2FA(steamID);
return await Response2FA(steamID).ConfigureAwait(false);
case "!2fano":
return await Response2FAConfirm(steamID, false).ConfigureAwait(false);
case "!2faoff":
return Response2FAOff(steamID);
case "!2faok":
return await Response2FAConfirm(steamID, true).ConfigureAwait(false);
case "!exit":
@@ -386,11 +388,9 @@ namespace ArchiSteamFarm {
string[] args = message.Split((char[]) null, StringSplitOptions.RemoveEmptyEntries);
switch (args[0]) {
case "!2fa":
return Response2FA(steamID, args[1]);
return await Response2FA(steamID, args[1]).ConfigureAwait(false);
case "!2fano":
return await Response2FAConfirm(steamID, args[1], false).ConfigureAwait(false);
case "!2faoff":
return Response2FAOff(steamID, args[1]);
case "!2faok":
return await Response2FAConfirm(steamID, args[1], true).ConfigureAwait(false);
case "!addlicense":
@@ -441,7 +441,7 @@ namespace ArchiSteamFarm {
}
// 2FA tokens are expiring soon, don't use limiter when user is providing one
if ((TwoFactorCode == null) || (BotDatabase.SteamGuardAccount != null)) {
if ((TwoFactorCode == null) || (BotDatabase.MobileAuthenticator != null)) {
await LimitLoginRequestsAsync().ConfigureAwait(false);
}
@@ -470,68 +470,38 @@ namespace ArchiSteamFarm {
}
private void ImportAuthenticator(string maFilePath) {
if ((BotDatabase.SteamGuardAccount != null) || !File.Exists(maFilePath)) {
if ((BotDatabase.MobileAuthenticator != null) || !File.Exists(maFilePath)) {
return;
}
Logging.LogGenericInfo("Converting SDA .maFile into ASF format...", BotName);
Logging.LogGenericInfo("Converting .maFile into ASF format...", BotName);
try {
BotDatabase.SteamGuardAccount = JsonConvert.DeserializeObject<SteamGuardAccount>(File.ReadAllText(maFilePath));
BotDatabase.MobileAuthenticator = JsonConvert.DeserializeObject<MobileAuthenticator>(File.ReadAllText(maFilePath));
File.Delete(maFilePath);
Logging.LogGenericInfo("Success!", BotName);
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
return;
}
// If this is SDA file, then we should already have everything ready
if (BotDatabase.SteamGuardAccount.Session != null) {
Logging.LogGenericInfo("Successfully finished importing mobile authenticator!", BotName);
if (BotDatabase.MobileAuthenticator == null) {
Logging.LogNullError(nameof(BotDatabase.MobileAuthenticator));
return;
}
// But here we're dealing with WinAuth authenticator
Logging.LogGenericInfo("ASF requires a few more steps to complete authenticator import...", BotName);
BotDatabase.MobileAuthenticator.Init(this);
if (!InitializeLoginAndPassword(true)) {
BotDatabase.SteamGuardAccount = null;
return;
}
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
LoginResult loginResult;
while ((loginResult = userLogin.DoLogin()) != LoginResult.LoginOkay) {
switch (loginResult) {
case LoginResult.Need2FA:
userLogin.TwoFactorCode = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
if (string.IsNullOrEmpty(userLogin.TwoFactorCode)) {
BotDatabase.SteamGuardAccount = null;
return;
}
break;
default:
BotDatabase.SteamGuardAccount = null;
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
return;
if (!BotDatabase.MobileAuthenticator.HasDeviceID) {
string deviceID = Program.GetUserInput(Program.EUserInputType.DeviceID, BotName);
if (string.IsNullOrEmpty(deviceID)) {
BotDatabase.MobileAuthenticator = null;
return;
}
BotDatabase.MobileAuthenticator.CorrectDeviceID(deviceID);
BotDatabase.Save();
}
if (userLogin.Session == null) {
BotDatabase.SteamGuardAccount = null;
Logging.LogGenericError("Session is invalid, linking can't be completed!", BotName);
return;
}
BotDatabase.SteamGuardAccount.FullyEnrolled = true;
BotDatabase.SteamGuardAccount.Session = userLogin.Session;
if (string.IsNullOrEmpty(BotDatabase.SteamGuardAccount.DeviceID)) {
BotDatabase.SteamGuardAccount.DeviceID = Program.GetUserInput(Program.EUserInputType.DeviceID, BotName);
}
BotDatabase.Save();
Logging.LogGenericInfo("Successfully finished importing mobile authenticator!", BotName);
}
@@ -657,9 +627,13 @@ namespace ArchiSteamFarm {
return "Trade couldn't be send because SteamMasterID is not defined!";
}
await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false);
HashSet<Steam.Item> inventory = await ArchiWebHandler.GetMyInventory(true).ConfigureAwait(false);
if (BotConfig.SteamMasterID == SteamClient.SteamID) {
return "You can't loot yourself!";
}
await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false);
HashSet<Steam.Item> inventory = await ArchiWebHandler.GetMyInventory(true).ConfigureAwait(false);
if ((inventory == null) || (inventory.Count == 0)) {
return "Nothing to send, inventory seems empty!";
}
@@ -676,7 +650,7 @@ namespace ArchiSteamFarm {
return "Trade offer failed due to error!";
}
await AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
await AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, BotConfig.SteamMasterID).ConfigureAwait(false);
return "Trade offer sent successfully!";
}
@@ -698,7 +672,7 @@ namespace ArchiSteamFarm {
return null;
}
private string Response2FA(ulong steamID) {
private async Task<string> Response2FA(ulong steamID) {
if (steamID == 0) {
Logging.LogNullError(nameof(steamID));
return null;
@@ -708,15 +682,15 @@ namespace ArchiSteamFarm {
return null;
}
if (BotDatabase.SteamGuardAccount == null) {
if (BotDatabase.MobileAuthenticator == null) {
return "That bot doesn't have ASF 2FA enabled!";
}
long timeLeft = 30 - TimeAligner.GetSteamTime() % 30;
return "2FA Token: " + BotDatabase.SteamGuardAccount.GenerateSteamGuardCode() + " (expires in " + timeLeft + " seconds)";
byte timeLeft = (byte) (30 - await BotDatabase.MobileAuthenticator.GetSteamTime().ConfigureAwait(false) % 30);
return "2FA Token: " + await BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false) + " (expires in " + timeLeft + " seconds)";
}
private static string Response2FA(ulong steamID, string botName) {
private static async Task<string> Response2FA(ulong steamID, string botName) {
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
Logging.LogNullError(nameof(steamID) + " || " + nameof(botName));
return null;
@@ -724,42 +698,7 @@ namespace ArchiSteamFarm {
Bot bot;
if (Bots.TryGetValue(botName, out bot)) {
return bot.Response2FA(steamID);
}
if (IsOwner(steamID)) {
return "Couldn't find any bot named " + botName + "!";
}
return null;
}
private string Response2FAOff(ulong steamID) {
if (steamID == 0) {
Logging.LogNullError(nameof(steamID));
return null;
}
if (!IsMaster(steamID)) {
return null;
}
if (BotDatabase.SteamGuardAccount == null) {
return "That bot doesn't have ASF 2FA enabled!";
}
return DelinkMobileAuthenticator() ? "Done! Bot is no longer using ASF 2FA" : "Something went wrong during delinking mobile authenticator!";
}
private static string Response2FAOff(ulong steamID, string botName) {
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
Logging.LogNullError(nameof(steamID) + " || " + nameof(botName));
return null;
}
Bot bot;
if (Bots.TryGetValue(botName, out bot)) {
return bot.Response2FAOff(steamID);
return await bot.Response2FA(steamID).ConfigureAwait(false);
}
if (IsOwner(steamID)) {
@@ -779,7 +718,7 @@ namespace ArchiSteamFarm {
return null;
}
if (BotDatabase.SteamGuardAccount == null) {
if (BotDatabase.MobileAuthenticator == null) {
return "That bot doesn't have ASF 2FA enabled!";
}
@@ -1392,101 +1331,6 @@ namespace ArchiSteamFarm {
}
}
private void LinkMobileAuthenticator() {
if (BotDatabase.SteamGuardAccount != null) {
return;
}
Logging.LogGenericInfo("Linking new ASF MobileAuthenticator...", BotName);
if (!InitializeLoginAndPassword(true)) {
return;
}
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
LoginResult loginResult;
while ((loginResult = userLogin.DoLogin()) != LoginResult.LoginOkay) {
switch (loginResult) {
case LoginResult.NeedEmail:
userLogin.EmailCode = Program.GetUserInput(Program.EUserInputType.SteamGuard, BotName);
if (string.IsNullOrEmpty(userLogin.EmailCode)) {
return;
}
break;
default:
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
return;
}
}
AuthenticatorLinker authenticatorLinker = new AuthenticatorLinker(userLogin.Session);
AuthenticatorLinker.LinkResult linkResult;
while ((linkResult = authenticatorLinker.AddAuthenticator()) != AuthenticatorLinker.LinkResult.AwaitingFinalization) {
switch (linkResult) {
case AuthenticatorLinker.LinkResult.MustProvidePhoneNumber:
authenticatorLinker.PhoneNumber = Program.GetUserInput(Program.EUserInputType.PhoneNumber, BotName);
if (string.IsNullOrEmpty(authenticatorLinker.PhoneNumber)) {
return;
}
break;
default:
Logging.LogGenericError("Unhandled situation: " + linkResult, BotName);
return;
}
}
BotDatabase.SteamGuardAccount = authenticatorLinker.LinkedAccount;
string sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName);
if (string.IsNullOrEmpty(sms)) {
Logging.LogGenericWarning("Aborted!", BotName);
DelinkMobileAuthenticator();
return;
}
AuthenticatorLinker.FinalizeResult finalizeResult;
while ((finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(sms)) != AuthenticatorLinker.FinalizeResult.Success) {
switch (finalizeResult) {
case AuthenticatorLinker.FinalizeResult.BadSMSCode:
sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName);
if (string.IsNullOrEmpty(sms)) {
Logging.LogGenericWarning("Aborted!", BotName);
DelinkMobileAuthenticator();
return;
}
break;
default:
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
DelinkMobileAuthenticator();
return;
}
}
// Ensure that we also save changes made by finalization step (if any)
BotDatabase.Save();
Logging.LogGenericInfo("Successfully linked ASF as new mobile authenticator for this account!", BotName);
Program.GetUserInput(Program.EUserInputType.RevocationCode, BotName, BotDatabase.SteamGuardAccount.RevocationCode);
}
private bool DelinkMobileAuthenticator() {
if (BotDatabase.SteamGuardAccount == null) {
return false;
}
// Try to deactivate authenticator, and assume we're safe to remove if it wasn't fully enrolled yet (even if request fails)
if (!BotDatabase.SteamGuardAccount.DeactivateAuthenticator() && BotDatabase.SteamGuardAccount.FullyEnrolled) {
return false;
}
BotDatabase.SteamGuardAccount = null;
return true;
}
private void JoinMasterChat() {
if (!SteamClient.IsConnected || (BotConfig.SteamMasterClanID == 0)) {
return;
@@ -1523,7 +1367,7 @@ namespace ArchiSteamFarm {
}
}
private void OnConnected(SteamClient.ConnectedCallback callback) {
private async void OnConnected(SteamClient.ConnectedCallback callback) {
if (callback == null) {
Logging.LogNullError(nameof(callback), BotName);
return;
@@ -1560,11 +1404,10 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Logging in...", BotName);
// If we have ASF 2FA enabled, we can always provide TwoFactorCode, and save a request
if (BotDatabase.SteamGuardAccount != null) {
TwoFactorCode = BotDatabase.SteamGuardAccount.GenerateSteamGuardCode();
if (BotDatabase.MobileAuthenticator != null) {
TwoFactorCode = await BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false);
}
// TODO: Please remove me immediately after https://github.com/SteamRE/SteamKit/issues/254 gets fixed
if (Program.GlobalConfig.HackIgnoreMachineID) {
Logging.LogGenericWarning("Using workaround for broken GenerateMachineID()!", BotName);
ArchiHandler.HackedLogOn(new SteamUser.LogOnDetails {
@@ -1581,17 +1424,31 @@ namespace ArchiSteamFarm {
return;
}
SteamUser.LogOn(new SteamUser.LogOnDetails {
Username = BotConfig.SteamLogin,
Password = BotConfig.SteamPassword,
AuthCode = AuthCode,
CellID = Program.GlobalDatabase.CellID,
LoginID = LoginID,
LoginKey = BotDatabase.LoginKey,
TwoFactorCode = TwoFactorCode,
SentryFileHash = sentryHash,
ShouldRememberPassword = true
});
try {
SteamUser.LogOn(new SteamUser.LogOnDetails {
Username = BotConfig.SteamLogin,
Password = BotConfig.SteamPassword,
AuthCode = AuthCode,
CellID = Program.GlobalDatabase.CellID,
LoginID = LoginID,
LoginKey = BotDatabase.LoginKey,
TwoFactorCode = TwoFactorCode,
SentryFileHash = sentryHash,
ShouldRememberPassword = true
});
} catch (Exception) {
ArchiHandler.HackedLogOn(new SteamUser.LogOnDetails {
Username = BotConfig.SteamLogin,
Password = BotConfig.SteamPassword,
AuthCode = AuthCode,
CellID = Program.GlobalDatabase.CellID,
LoginID = LoginID,
LoginKey = BotDatabase.LoginKey,
TwoFactorCode = TwoFactorCode,
SentryFileHash = sentryHash,
ShouldRememberPassword = true
});
}
}
private async void OnDisconnected(SteamClient.DisconnectedCallback callback) {
@@ -1628,7 +1485,7 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Reconnecting...", BotName);
// 2FA tokens are expiring soon, don't use limiter when user is providing one
if ((TwoFactorCode == null) || (BotDatabase.SteamGuardAccount != null)) {
if ((TwoFactorCode == null) || (BotDatabase.MobileAuthenticator != null)) {
await LimitLoginRequestsAsync().ConfigureAwait(false);
}
@@ -1716,18 +1573,10 @@ namespace ArchiSteamFarm {
}
foreach (SteamFriends.FriendsListCallback.Friend friend in callback.FriendList.Where(friend => friend.Relationship == EFriendRelationship.RequestRecipient)) {
switch (friend.SteamID.AccountType) {
case EAccountType.Clan:
// TODO: Accept clan invites from master?
break;
default:
if (IsMaster(friend.SteamID)) {
SteamFriends.AddFriend(friend.SteamID);
} else if (BotConfig.IsBotAccount) {
SteamFriends.RemoveFriend(friend.SteamID);
}
break;
if (IsMaster(friend.SteamID)) {
SteamFriends.AddFriend(friend.SteamID);
} else if (BotConfig.IsBotAccount) {
SteamFriends.RemoveFriend(friend.SteamID);
}
}
}
@@ -1812,7 +1661,7 @@ namespace ArchiSteamFarm {
break;
case EResult.AccountLoginDeniedNeedTwoFactor:
if (BotDatabase.SteamGuardAccount == null) {
if (BotDatabase.MobileAuthenticator == null) {
TwoFactorCode = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
if (string.IsNullOrEmpty(TwoFactorCode)) {
Stop();
@@ -1835,13 +1684,11 @@ namespace ArchiSteamFarm {
Program.GlobalDatabase.CellID = callback.CellID;
}
if (BotDatabase.SteamGuardAccount == null) {
if (BotDatabase.MobileAuthenticator == null) {
// Support and convert SDA files
string maFilePath = Path.Combine(Program.ConfigDirectory, callback.ClientSteamID.ConvertToUInt64() + ".maFile");
if (File.Exists(maFilePath)) {
ImportAuthenticator(maFilePath);
} else if ((TwoFactorCode == null) && BotConfig.UseAsfAsMobileAuthenticator) {
LinkMobileAuthenticator();
}
}
@@ -1892,7 +1739,7 @@ namespace ArchiSteamFarm {
Logging.LogGenericWarning("Unable to login to Steam: " + callback.Result, BotName);
break;
default: // Unexpected result, shutdown immediately
Logging.LogGenericWarning("Unable to login to Steam: " + callback.Result, BotName);
Logging.LogGenericError("Unable to login to Steam: " + callback.Result, BotName);
Stop();
break;
}

View File

@@ -83,9 +83,6 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)]
internal bool DistributeKeys { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool UseAsfAsMobileAuthenticator { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool ShutdownOnFarmingFinished { get; private set; } = false;

View File

@@ -23,13 +23,15 @@
*/
using Newtonsoft.Json;
using SteamAuth;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace ArchiSteamFarm {
internal sealed class BotDatabase {
[JsonProperty]
private string _LoginKey;
internal string LoginKey {
get {
return _LoginKey;
@@ -44,7 +46,28 @@ namespace ArchiSteamFarm {
}
}
internal SteamGuardAccount SteamGuardAccount {
[JsonProperty]
private MobileAuthenticator _MobileAuthenticator;
internal MobileAuthenticator MobileAuthenticator {
get {
return _MobileAuthenticator;
}
set {
if (_MobileAuthenticator == value) {
return;
}
_MobileAuthenticator = value;
Save();
}
}
// TODO: Converter code will be removed soon
[JsonProperty]
private ObsoleteSteamGuardAccount _SteamGuardAccount;
internal ObsoleteSteamGuardAccount SteamGuardAccount {
get {
return _SteamGuardAccount;
}
@@ -58,12 +81,6 @@ namespace ArchiSteamFarm {
}
}
[JsonProperty]
private string _LoginKey;
[JsonProperty]
private SteamGuardAccount _SteamGuardAccount;
private string FilePath;
internal static BotDatabase Load(string filePath) {

View File

@@ -51,6 +51,11 @@ namespace ArchiSteamFarm {
}
public void WriteLine(string category, string msg) {
if (string.IsNullOrEmpty(category) && string.IsNullOrEmpty(msg)) {
Logging.LogNullError(nameof(category) + " && " + nameof(msg));
return;
}
lock (FilePath) {
try {
File.AppendAllText(FilePath, category + " | " + msg + Environment.NewLine);

View File

@@ -159,6 +159,11 @@ namespace ArchiSteamFarm {
globalConfig.FarmingDelay = DefaultFarmingDelay;
}
if ((globalConfig.FarmingDelay > 5) && Mono.RequiresWorkaroundForBug41701()) {
Logging.LogGenericWarning("Your Mono runtime is affected by bug 41701, FarmingDelay of " + globalConfig.FarmingDelay + " is not possible - value of 5 will be used instead");
globalConfig.FarmingDelay = 5;
}
if (globalConfig.HttpTimeout == 0) {
Logging.LogGenericWarning("Configured HttpTimeout is invalid: " + globalConfig.HttpTimeout + ". Value of " + DefaultHttpTimeout + " will be used instead");
globalConfig.HttpTimeout = DefaultHttpTimeout;

View File

@@ -28,7 +28,8 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm.JSON {
internal static class GitHub {
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global"), SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
internal sealed class ReleaseResponse {
internal sealed class Asset {
[JsonProperty(PropertyName = "name", Required = Required.Always)]

View File

@@ -25,6 +25,8 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using HtmlAgilityPack;
using Newtonsoft.Json;
using SteamKit2;
@@ -50,7 +52,8 @@ namespace ArchiSteamFarm.JSON {
internal uint AppID { get; set; }
[JsonProperty(PropertyName = "appid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
[JsonProperty(PropertyName = "appid", Required = Required.DisallowNull)]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private string AppIDString {
get {
return AppID.ToString();
@@ -72,7 +75,8 @@ namespace ArchiSteamFarm.JSON {
internal ulong ContextID { get; set; }
[JsonProperty(PropertyName = "contextid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
[JsonProperty(PropertyName = "contextid", Required = Required.DisallowNull)]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private string ContextIDString {
get {
return ContextID.ToString();
@@ -114,7 +118,8 @@ namespace ArchiSteamFarm.JSON {
}
}
[JsonProperty(PropertyName = "id", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
[JsonProperty(PropertyName = "id", Required = Required.DisallowNull)]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private string ID {
get { return AssetIDString; }
set { AssetIDString = value; }
@@ -122,7 +127,8 @@ namespace ArchiSteamFarm.JSON {
internal ulong ClassID { get; set; }
[JsonProperty(PropertyName = "classid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
[JsonProperty(PropertyName = "classid", Required = Required.DisallowNull)]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private string ClassIDString {
get {
return ClassID.ToString();
@@ -142,9 +148,10 @@ namespace ArchiSteamFarm.JSON {
}
}
internal ulong InstanceID { get; set; }
internal ulong InstanceID { private get; set; }
[JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
[JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull)]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private string InstanceIDString {
get {
return InstanceID.ToString();
@@ -166,7 +173,8 @@ namespace ArchiSteamFarm.JSON {
internal uint Amount { get; set; }
[JsonProperty(PropertyName = "amount", Required = Required.Always), SuppressMessage("ReSharper", "UnusedMember.Local")]
[JsonProperty(PropertyName = "amount", Required = Required.Always)]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private string AmountString {
get {
return Amount.ToString();
@@ -209,7 +217,8 @@ namespace ArchiSteamFarm.JSON {
internal ulong TradeOfferID { get; set; }
[JsonProperty(PropertyName = "tradeofferid", Required = Required.Always), SuppressMessage("ReSharper", "UnusedMember.Local")]
[JsonProperty(PropertyName = "tradeofferid", Required = Required.Always)]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private string TradeOfferIDString {
get {
return TradeOfferID.ToString();
@@ -244,7 +253,7 @@ namespace ArchiSteamFarm.JSON {
// Extra
internal ulong OtherSteamID64 => OtherSteamID3 == 0 ? 0 : new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
internal bool IsSteamCardsOnlyTradeForUs() => ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamContextID) && ((item.Type == Item.EType.FoilTradingCard) || (item.Type == Item.EType.TradingCard)));
internal bool IsSteamCardsOnlyTradeForUs() => ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamContextID) && (item.Type == Item.EType.TradingCard));
internal bool IsPotentiallyDupesTradeForUs() {
Dictionary<uint, Dictionary<Item.EType, uint>> itemsToGivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
@@ -321,5 +330,184 @@ namespace ArchiSteamFarm.JSON {
[JsonProperty(PropertyName = "them", Required = Required.Always)]
internal ItemList ItemsToReceive { get; } = new ItemList();
}
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
internal sealed class ConfirmationResponse {
[JsonProperty(PropertyName = "success", Required = Required.Always)]
internal bool Success { get; private set; }
}
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
internal sealed class ConfirmationDetails {
internal enum EType : byte {
Unknown,
Trade,
Market,
Other
}
[JsonProperty(PropertyName = "success", Required = Required.Always)]
internal bool Success { get; private set; }
private EType _Type;
private EType Type {
get {
if (_Type != EType.Unknown) {
return _Type;
}
if (HtmlDocument == null) {
Logging.LogNullError(nameof(HtmlDocument));
return EType.Unknown;
}
HtmlNode testNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='mobileconf_listing_prices']");
if (testNode != null) {
_Type = EType.Market;
return _Type;
}
testNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='mobileconf_trade_area']");
if (testNode != null) {
_Type = EType.Trade;
return _Type;
}
_Type = EType.Other;
return _Type;
}
}
private ulong _TradeOfferID;
internal ulong TradeOfferID {
get {
if (_TradeOfferID != 0) {
return _TradeOfferID;
}
if (Type != EType.Trade) {
return 0;
}
if (HtmlDocument == null) {
Logging.LogNullError(nameof(HtmlDocument));
return 0;
}
HtmlNode htmlNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='tradeoffer']");
if (htmlNode == null) {
Logging.LogNullError(nameof(htmlNode));
return 0;
}
string id = htmlNode.GetAttributeValue("id", null);
if (string.IsNullOrEmpty(id)) {
Logging.LogNullError(nameof(id));
return 0;
}
int index = id.IndexOf('_');
if (index < 0) {
Logging.LogNullError(nameof(index));
return 0;
}
index++;
if (id.Length <= index) {
Logging.LogNullError(nameof(id.Length));
return 0;
}
id = id.Substring(index);
if (ulong.TryParse(id, out _TradeOfferID) && (_TradeOfferID != 0)) {
return _TradeOfferID;
}
Logging.LogNullError(nameof(_TradeOfferID));
return 0;
}
}
private ulong _OtherSteamID64;
internal ulong OtherSteamID64 {
get {
if (_OtherSteamID64 != 0) {
return _OtherSteamID64;
}
if (Type != EType.Trade) {
return 0;
}
if (OtherSteamID3 == 0) {
Logging.LogNullError(nameof(OtherSteamID3));
return 0;
}
_OtherSteamID64 = new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
return _OtherSteamID64;
}
}
[JsonProperty(PropertyName = "html", Required = Required.Always)]
private string HTML;
private uint _OtherSteamID3;
private uint OtherSteamID3 {
get {
if (_OtherSteamID3 != 0) {
return _OtherSteamID3;
}
if (Type != EType.Trade) {
return 0;
}
if (HtmlDocument == null) {
Logging.LogNullError(nameof(HtmlDocument));
return 0;
}
HtmlNode htmlNode = HtmlDocument.DocumentNode.SelectSingleNode("//a/@data-miniprofile");
if (htmlNode == null) {
Logging.LogNullError(nameof(htmlNode));
return 0;
}
string miniProfile = htmlNode.GetAttributeValue("data-miniprofile", null);
if (string.IsNullOrEmpty(miniProfile)) {
Logging.LogNullError(nameof(miniProfile));
return 0;
}
if (uint.TryParse(miniProfile, out _OtherSteamID3) && (_OtherSteamID3 != 0)) {
return _OtherSteamID3;
}
Logging.LogNullError(nameof(_OtherSteamID3));
return 0;
}
}
private HtmlDocument _HtmlDocument;
private HtmlDocument HtmlDocument {
get {
if (_HtmlDocument != null) {
return _HtmlDocument;
}
if (string.IsNullOrEmpty(HTML)) {
Logging.LogNullError(nameof(HTML));
return null;
}
_HtmlDocument = new HtmlDocument();
_HtmlDocument.LoadHtml(WebUtility.HtmlDecode(HTML));
return _HtmlDocument;
}
}
}
}
}

View File

@@ -23,7 +23,6 @@
*/
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
@@ -123,7 +122,7 @@ namespace ArchiSteamFarm {
}
}
[Conditional("DEBUG"), SuppressMessage("ReSharper", "UnusedMember.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message), botName);

View File

@@ -0,0 +1,289 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.JSON;
using HtmlAgilityPack;
using Newtonsoft.Json;
namespace ArchiSteamFarm {
internal sealed class MobileAuthenticator {
internal sealed class Confirmation {
internal readonly uint ID;
internal readonly ulong Key;
internal readonly Steam.ConfirmationDetails.EType Type;
internal Confirmation(uint id, ulong key, Steam.ConfirmationDetails.EType type) {
if ((id == 0) || (key == 0) || (type == Steam.ConfirmationDetails.EType.Unknown)) {
throw new ArgumentNullException(nameof(id) + " || " + nameof(key) + " || " + nameof(type));
}
ID = id;
Key = key;
Type = type;
}
}
private static readonly byte[] TokenCharacters = { 50, 51, 52, 53, 54, 55, 56, 57, 66, 67, 68, 70, 71, 72, 74, 75, 77, 78, 80, 81, 82, 84, 86, 87, 88, 89 };
private static readonly SemaphoreSlim TimeSemaphore = new SemaphoreSlim(1);
private static short SteamTimeDifference;
internal bool HasDeviceID => !string.IsNullOrEmpty(DeviceID);
[JsonProperty(PropertyName = "shared_secret", Required = Required.DisallowNull)]
private string SharedSecret;
[JsonProperty(PropertyName = "identity_secret", Required = Required.DisallowNull)]
private string IdentitySecret;
[JsonProperty(PropertyName = "device_id")]
private string DeviceID;
private Bot Bot;
internal static MobileAuthenticator LoadFromSteamGuardAccount(ObsoleteSteamGuardAccount sga) {
if (sga != null) {
return new MobileAuthenticator {
SharedSecret = sga.SharedSecret,
IdentitySecret = sga.IdentitySecret,
DeviceID = sga.DeviceID
};
}
Logging.LogNullError(nameof(sga));
return null;
}
private MobileAuthenticator() {
}
internal void Init(Bot bot) {
if (bot == null) {
throw new ArgumentNullException(nameof(bot));
}
Bot = bot;
}
internal void CorrectDeviceID(string deviceID) {
if (string.IsNullOrEmpty(deviceID)) {
Logging.LogNullError(nameof(deviceID), Bot.BotName);
return;
}
DeviceID = deviceID;
}
internal async Task<bool> HandleConfirmation(Confirmation confirmation, bool accept) {
if (confirmation == null) {
Logging.LogNullError(nameof(confirmation), Bot.BotName);
return false;
}
uint time = await GetSteamTime().ConfigureAwait(false);
if (time == 0) {
Logging.LogNullError(nameof(time), Bot.BotName);
return false;
}
string confirmationHash = GenerateConfirmationKey(time, "conf");
if (!string.IsNullOrEmpty(confirmationHash)) {
return await Bot.ArchiWebHandler.HandleConfirmation(DeviceID, confirmationHash, time, confirmation.ID, confirmation.Key, accept);
}
Logging.LogNullError(nameof(confirmationHash), Bot.BotName);
return false;
}
internal async Task<Steam.ConfirmationDetails> GetConfirmationDetails(Confirmation confirmation) {
if (confirmation == null) {
Logging.LogNullError(nameof(confirmation), Bot.BotName);
return null;
}
uint time = await GetSteamTime().ConfigureAwait(false);
if (time == 0) {
Logging.LogNullError(nameof(time), Bot.BotName);
return null;
}
string confirmationHash = GenerateConfirmationKey(time, "conf");
if (!string.IsNullOrEmpty(confirmationHash)) {
return await Bot.ArchiWebHandler.GetConfirmationDetails(DeviceID, confirmationHash, time, confirmation.ID);
}
Logging.LogNullError(nameof(confirmationHash), Bot.BotName);
return null;
}
internal async Task<string> GenerateToken() {
uint time = await GetSteamTime().ConfigureAwait(false);
if (time != 0) {
return GenerateTokenForTime(time);
}
Logging.LogNullError(nameof(time), Bot.BotName);
return null;
}
internal async Task<HashSet<Confirmation>> GetConfirmations() {
uint time = await GetSteamTime().ConfigureAwait(false);
if (time == 0) {
Logging.LogNullError(nameof(time), Bot.BotName);
return null;
}
string confirmationHash = GenerateConfirmationKey(time, "conf");
if (string.IsNullOrEmpty(confirmationHash)) {
Logging.LogNullError(nameof(confirmationHash), Bot.BotName);
return null;
}
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetConfirmations(DeviceID, confirmationHash, time);
if (htmlDocument == null) {
return null;
}
HtmlNodeCollection confirmationNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='mobileconf_list_entry']");
if (confirmationNodes == null) {
return null;
}
HashSet<Confirmation> result = new HashSet<Confirmation>();
foreach (HtmlNode confirmationNode in confirmationNodes) {
string idString = confirmationNode.GetAttributeValue("data-confid", null);
if (string.IsNullOrEmpty(idString)) {
Logging.LogNullError(nameof(idString), Bot.BotName);
continue;
}
uint id;
if (!uint.TryParse(idString, out id) || (id == 0)) {
Logging.LogNullError(nameof(id), Bot.BotName);
continue;
}
string keyString = confirmationNode.GetAttributeValue("data-key", null);
if (string.IsNullOrEmpty(keyString)) {
Logging.LogNullError(nameof(keyString), Bot.BotName);
continue;
}
ulong key;
if (!ulong.TryParse(keyString, out key) || (key == 0)) {
Logging.LogNullError(nameof(key), Bot.BotName);
continue;
}
HtmlNode descriptionNode = confirmationNode.SelectSingleNode(".//div[@class='mobileconf_list_entry_description']/div");
if (descriptionNode == null) {
Logging.LogNullError(nameof(descriptionNode), Bot.BotName);
continue;
}
Steam.ConfirmationDetails.EType type;
string description = descriptionNode.InnerText;
if (description.Equals("Sell - Market Listing")) {
type = Steam.ConfirmationDetails.EType.Market;
} else if (description.StartsWith("Trade with ", StringComparison.Ordinal)) {
type = Steam.ConfirmationDetails.EType.Trade;
} else {
type = Steam.ConfirmationDetails.EType.Other;
}
result.Add(new Confirmation(id, key, type));
}
return result;
}
internal async Task<uint> GetSteamTime() {
if (SteamTimeDifference != 0) {
return (uint) (Utilities.GetUnixTime() + SteamTimeDifference);
}
await TimeSemaphore.WaitAsync().ConfigureAwait(false);
if (SteamTimeDifference == 0) {
uint serverTime = Bot.ArchiWebHandler.GetServerTime();
if (serverTime != 0) {
SteamTimeDifference = (short) (serverTime - Utilities.GetUnixTime());
}
}
TimeSemaphore.Release();
return (uint) (Utilities.GetUnixTime() + SteamTimeDifference);
}
private string GenerateTokenForTime(long time) {
if (time == 0) {
Logging.LogNullError(nameof(time), Bot.BotName);
return null;
}
byte[] sharedSecretArray = Convert.FromBase64String(SharedSecret);
byte[] timeArray = new byte[8];
time /= 30L;
for (int i = 8; i > 0; i--) {
timeArray[i - 1] = (byte) time;
time >>= 8;
}
byte[] hashedData;
using (HMACSHA1 hmacGenerator = new HMACSHA1(sharedSecretArray, true)) {
hashedData = hmacGenerator.ComputeHash(timeArray);
}
byte b = (byte) (hashedData[19] & 0xF);
int codePoint = ((hashedData[b] & 0x7F) << 24) | ((hashedData[b + 1] & 0xFF) << 16) | ((hashedData[b + 2] & 0xFF) << 8) | (hashedData[b + 3] & 0xFF);
byte[] codeArray = new byte[5];
for (int i = 0; i < 5; ++i) {
codeArray[i] = TokenCharacters[codePoint % TokenCharacters.Length];
codePoint /= TokenCharacters.Length;
}
return Encoding.UTF8.GetString(codeArray);
}
private string GenerateConfirmationKey(uint time, string tag = null) {
if (time == 0) {
Logging.LogNullError(nameof(time), Bot.BotName);
return null;
}
byte[] b64Secret = Convert.FromBase64String(IdentitySecret);
int bufferSize = 8;
if (string.IsNullOrEmpty(tag) == false) {
bufferSize += Math.Min(32, tag.Length);
}
byte[] buffer = new byte[bufferSize];
byte[] timeArray = BitConverter.GetBytes((long) time);
if (BitConverter.IsLittleEndian) {
Array.Reverse(timeArray);
}
Array.Copy(timeArray, buffer, 8);
if (string.IsNullOrEmpty(tag) == false) {
Array.Copy(Encoding.UTF8.GetBytes(tag), 0, buffer, 8, bufferSize - 8);
}
byte[] hash;
using (HMACSHA1 hmac = new HMACSHA1(b64Secret, true)) {
hash = hmac.ComputeHash(buffer);
}
return Convert.ToBase64String(hash, Base64FormattingOptions.None);
}
}
}

75
ArchiSteamFarm/Mono.cs Normal file
View File

@@ -0,0 +1,75 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System;
using System.Reflection;
namespace ArchiSteamFarm {
internal static class Mono {
internal static bool RequiresWorkaroundForBug41701() {
// https://bugzilla.xamarin.com/show_bug.cgi?id=41701
Version version = GetMonoVersion();
if (version == null) {
return false;
}
return version >= new Version(4, 4);
}
private static Version GetMonoVersion() {
Type type = Type.GetType("Mono.Runtime");
if (type == null) {
return null; // OK, not Mono
}
MethodInfo displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
if (displayName == null) {
Logging.LogNullError(nameof(displayName));
return null;
}
string versionString = (string) displayName.Invoke(null, null);
if (string.IsNullOrEmpty(versionString)) {
Logging.LogNullError(nameof(versionString));
return null;
}
int index = versionString.IndexOf(' ');
if (index <= 0) {
Logging.LogNullError(nameof(index));
return null;
}
versionString = versionString.Substring(0, index);
Version version;
if (Version.TryParse(versionString, out version)) {
return version;
}
Logging.LogNullError(nameof(version));
return null;
}
}
}

View File

@@ -0,0 +1,43 @@
using Newtonsoft.Json;
namespace ArchiSteamFarm {
// TODO: This will be completely removed soon
public class ObsoleteSteamGuardAccount {
[JsonProperty("shared_secret")]
public string SharedSecret { get; set; }
[JsonProperty("serial_number")]
public string SerialNumber { get; set; }
[JsonProperty("revocation_code")]
public string RevocationCode { get; set; }
[JsonProperty("uri")]
public string URI { get; set; }
[JsonProperty("server_time")]
public long ServerTime { get; set; }
[JsonProperty("account_name")]
public string AccountName { get; set; }
[JsonProperty("token_gid")]
public string TokenGID { get; set; }
[JsonProperty("identity_secret")]
public string IdentitySecret { get; set; }
[JsonProperty("secret_1")]
public string Secret1 { get; set; }
[JsonProperty("status")]
public int Status { get; set; }
[JsonProperty("device_id")]
public string DeviceID { get; set; }
[JsonProperty("fully_enrolled")]
public bool FullyEnrolled { get; set; }
}
}

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.5.4")]
[assembly: AssemblyFileVersion("2.0.5.4")]
[assembly: AssemblyVersion("2.1.0.0")]
[assembly: AssemblyFileVersion("2.1.0.0")]

View File

@@ -22,7 +22,6 @@
*/
using SteamAuth;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -95,7 +94,11 @@ namespace ArchiSteamFarm {
}
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
if (tradeOffers.Any(tradeoffer => tradeoffer.ItemsToGive.Count > 0)) {
HashSet<ulong> tradeIDs = new HashSet<ulong>(tradeOffers.Select(tradeOffer => tradeOffer.TradeOfferID));
await Bot.AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, 0, tradeIDs).ConfigureAwait(false);
}
}
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
@@ -152,7 +155,18 @@ namespace ArchiSteamFarm {
}
// At this point we're sure that STM trade is valid
// If we're dealing with special cards with short lifespan, accept the trade only if user doesn't have trade holds
if (tradeOffer.ItemsToGive.Any(item => GlobalConfig.GlobalBlacklist.Contains(item.RealAppID))) {
byte? holdDuration = await Bot.ArchiWebHandler.GetTradeHoldDuration(tradeOffer.TradeOfferID).ConfigureAwait(false);
if (holdDuration.GetValueOrDefault() > 0) {
return false;
}
}
// Now check if it's worth for us to do the trade
await LimitInventoryRequestsAsync().ConfigureAwait(false);
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMyInventory(false).ConfigureAwait(false);
if ((inventory == null) || (inventory.Count == 0)) {
return true; // OK, assume that this trade is valid, we can't check our EQ

View File

@@ -62,6 +62,8 @@ namespace ArchiSteamFarm {
return cookies.Count == 0 ? null : (from Cookie cookie in cookies where cookie.Name.Equals(name) select cookie.Value).FirstOrDefault();
}
internal static uint GetUnixTime() => (uint) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
internal static Task SleepAsync(int miliseconds) {
if (miliseconds >= 0) {
return Task.Delay(miliseconds);

View File

@@ -413,7 +413,12 @@ namespace ArchiSteamFarm {
try {
responseMessage = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false);
} catch { // Request failed, we don't need to know the exact reason, swallow exception
} catch (Exception e) {
// This exception is really common, don't bother with it unless debug mode is enabled
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
Logging.LogGenericException(e, Identifier);
}
return null;
}
}

View File

@@ -16,7 +16,6 @@
"SteamTradeMatcher": false,
"ForwardKeysToOtherBots": false,
"DistributeKeys": false,
"UseAsfAsMobileAuthenticator": false,
"ShutdownOnFarmingFinished": false,
"SendOnFarmingFinished": false,
"SteamTradeToken": null,

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
</configuration>
</configuration>

View File

@@ -87,9 +87,6 @@ namespace ConfigGenerator {
[JsonProperty(Required = Required.DisallowNull)]
public bool DistributeKeys { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool UseAsfAsMobileAuthenticator { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool ShutdownOnFarmingFinished { get; set; } = false;

View File

@@ -9,9 +9,10 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ConfigGenerator</RootNamespace>
<AssemblyName>ConfigGenerator</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>

View File

@@ -9,18 +9,18 @@
//------------------------------------------------------------------------------
namespace ConfigGenerator.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings) (global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
</configuration>

View File

@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GUI</RootNamespace>
<AssemblyName>GUI</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>

View File

@@ -1,11 +1,14 @@
ArchiSteamFarm
===================
[![Build Status (Windows)](https://img.shields.io/appveyor/ci/JustArchi/ArchiSteamFarm.svg?label=Windows)](https://ci.appveyor.com/project/JustArchi/ArchiSteamFarm)
[![Build Status (Mono)](https://img.shields.io/travis/JustArchi/ArchiSteamFarm.svg?label=Mono)](https://travis-ci.org/JustArchi/ArchiSteamFarm)
[![GitHub Release](https://img.shields.io/github/release/JustArchi/ArchiSteamFarm.svg?label=Latest)](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)
[![Github All Releases](https://img.shields.io/github/downloads/JustArchi/ArchiSteamFarm/total.svg?label=Downloads)](https://github.com/JustArchi/ArchiSteamFarm/releases)
[![Paypal Donate](https://img.shields.io/badge/Paypal-donate-yellow.svg)](https://www.paypal.me/JustArchi/1usd)
[![Gitter](https://img.shields.io/gitter/room/JustArchi/ArchiSteamFarm.svg?label=Chat&maxAge=60)](https://gitter.im/JustArchi/ArchiSteamFarm)
[![Build Status (Windows)](https://img.shields.io/appveyor/ci/JustArchi/ArchiSteamFarm.svg?label=Windows&maxAge=60)](https://ci.appveyor.com/project/JustArchi/ArchiSteamFarm)
[![Build Status (Mono)](https://img.shields.io/travis/JustArchi/ArchiSteamFarm.svg?label=Mono&maxAge=60)](https://travis-ci.org/JustArchi/ArchiSteamFarm)
[![License](https://img.shields.io/github/license/JustArchi/ArchiSteamFarm.svg?label=License&maxAge=86400)](./LICENSE-2.0.txt)
[![GitHub Release](https://img.shields.io/github/release/JustArchi/ArchiSteamFarm.svg?label=Latest&maxAge=60)](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)
[![Github All Releases](https://img.shields.io/github/downloads/JustArchi/ArchiSteamFarm/total.svg?label=Downloads&maxAge=60)](https://github.com/JustArchi/ArchiSteamFarm/releases)
[![Paypal Donate](https://img.shields.io/badge/PayPal-donate-yellow.svg)](https://www.paypal.me/JustArchi/1usd)
[![Steam Donate](https://img.shields.io/badge/Steam-donate-yellow.svg)](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)
---

View File

@@ -1,12 +0,0 @@
namespace SteamAuth
{
public static class APIEndpoints
{
public const string STEAMAPI_BASE = "https://api.steampowered.com";
public const string COMMUNITY_BASE = "https://steamcommunity.com";
public const string MOBILEAUTH_BASE = STEAMAPI_BASE + "/IMobileAuthService/%s/v0001";
public static string MOBILEAUTH_GETWGTOKEN = MOBILEAUTH_BASE.Replace("%s", "GetWGToken");
public const string TWO_FACTOR_BASE = STEAMAPI_BASE + "/ITwoFactorService/%s/v0001";
public static string TWO_FACTOR_TIME_QUERY = TWO_FACTOR_BASE.Replace("%s", "QueryTime");
}
}

View File

@@ -1,292 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace SteamAuth
{
/// <summary>
/// Handles the linking process for a new mobile authenticator.
/// </summary>
public class AuthenticatorLinker
{
/// <summary>
/// Set to register a new phone number when linking. If a phone number is not set on the account, this must be set. If a phone number is set on the account, this must be null.
/// </summary>
public string PhoneNumber = null;
/// <summary>
/// Randomly-generated device ID. Should only be generated once per linker.
/// </summary>
public string DeviceID { get; private set; }
/// <summary>
/// After the initial link step, if successful, this will be the SteamGuard data for the account. PLEASE save this somewhere after generating it; it's vital data.
/// </summary>
public SteamGuardAccount LinkedAccount { get; private set; }
/// <summary>
/// True if the authenticator has been fully finalized.
/// </summary>
public bool Finalized = false;
private SessionData _session;
private CookieContainer _cookies;
public AuthenticatorLinker(SessionData session)
{
this._session = session;
this.DeviceID = GenerateDeviceID();
this._cookies = new CookieContainer();
session.AddCookies(_cookies);
}
public LinkResult AddAuthenticator()
{
bool hasPhone = _hasPhoneAttached();
if (hasPhone && PhoneNumber != null)
return LinkResult.MustRemovePhoneNumber;
if (!hasPhone && PhoneNumber == null)
return LinkResult.MustProvidePhoneNumber;
if (!hasPhone)
{
if (!_addPhoneNumber())
{
return LinkResult.GeneralFailure;
}
}
var postData = new NameValueCollection();
postData.Add("access_token", _session.OAuthToken);
postData.Add("steamid", _session.SteamID.ToString());
postData.Add("authenticator_type", "1");
postData.Add("device_identifier", this.DeviceID);
postData.Add("sms_phone_id", "1");
string response = SteamWeb.MobileLoginRequest(APIEndpoints.STEAMAPI_BASE + "/ITwoFactorService/AddAuthenticator/v0001", "POST", postData);
if (response == null) return LinkResult.GeneralFailure;
var addAuthenticatorResponse = JsonConvert.DeserializeObject<AddAuthenticatorResponse>(response);
if (addAuthenticatorResponse == null || addAuthenticatorResponse.Response == null)
{
return LinkResult.GeneralFailure;
}
if (addAuthenticatorResponse.Response.Status == 29)
{
return LinkResult.AuthenticatorPresent;
}
if (addAuthenticatorResponse.Response.Status != 1)
{
return LinkResult.GeneralFailure;
}
this.LinkedAccount = addAuthenticatorResponse.Response;
LinkedAccount.Session = this._session;
LinkedAccount.DeviceID = this.DeviceID;
return LinkResult.AwaitingFinalization;
}
public FinalizeResult FinalizeAddAuthenticator(string smsCode)
{
//The act of checking the SMS code is necessary for Steam to finalize adding the phone number to the account.
//Of course, we only want to check it if we're adding a phone number in the first place...
if (!String.IsNullOrEmpty(this.PhoneNumber) && !this._checkSMSCode(smsCode))
{
return FinalizeResult.BadSMSCode;
}
var postData = new NameValueCollection();
postData.Add("steamid", _session.SteamID.ToString());
postData.Add("access_token", _session.OAuthToken);
postData.Add("activation_code", smsCode);
int tries = 0;
while (tries <= 30)
{
postData.Set("authenticator_code", LinkedAccount.GenerateSteamGuardCode());
postData.Set("authenticator_time", TimeAligner.GetSteamTime().ToString());
string response = SteamWeb.MobileLoginRequest(APIEndpoints.STEAMAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", postData);
if (response == null) return FinalizeResult.GeneralFailure;
var finalizeResponse = JsonConvert.DeserializeObject<FinalizeAuthenticatorResponse>(response);
if (finalizeResponse == null || finalizeResponse.Response == null)
{
return FinalizeResult.GeneralFailure;
}
if (finalizeResponse.Response.Status == 89)
{
return FinalizeResult.BadSMSCode;
}
if (finalizeResponse.Response.Status == 88)
{
if (tries >= 30)
{
return FinalizeResult.UnableToGenerateCorrectCodes;
}
}
if (!finalizeResponse.Response.Success)
{
return FinalizeResult.GeneralFailure;
}
if (finalizeResponse.Response.WantMore)
{
tries++;
continue;
}
this.LinkedAccount.FullyEnrolled = true;
return FinalizeResult.Success;
}
return FinalizeResult.GeneralFailure;
}
private bool _checkSMSCode(string smsCode)
{
var postData = new NameValueCollection();
postData.Add("op", "check_sms_code");
postData.Add("arg", smsCode);
postData.Add("sessionid", _session.SessionID);
string response = SteamWeb.Request(APIEndpoints.COMMUNITY_BASE + "/steamguard/phoneajax", "POST", postData, _cookies);
if (response == null) return false;
var addPhoneNumberResponse = JsonConvert.DeserializeObject<AddPhoneResponse>(response);
return addPhoneNumberResponse.Success;
}
private bool _addPhoneNumber()
{
var postData = new NameValueCollection();
postData.Add("op", "add_phone_number");
postData.Add("arg", PhoneNumber);
postData.Add("sessionid", _session.SessionID);
string response = SteamWeb.Request(APIEndpoints.COMMUNITY_BASE + "/steamguard/phoneajax", "POST", postData, _cookies);
if (response == null) return false;
var addPhoneNumberResponse = JsonConvert.DeserializeObject<AddPhoneResponse>(response);
return addPhoneNumberResponse.Success;
}
private bool _hasPhoneAttached()
{
var postData = new NameValueCollection();
postData.Add("op", "has_phone");
postData.Add("arg", "null");
postData.Add("sessionid", _session.SessionID);
string response = SteamWeb.Request(APIEndpoints.COMMUNITY_BASE + "/steamguard/phoneajax", "POST", postData, _cookies);
if (response == null) return false;
var hasPhoneResponse = JsonConvert.DeserializeObject<HasPhoneResponse>(response);
return hasPhoneResponse.HasPhone;
}
public enum LinkResult
{
MustProvidePhoneNumber, //No phone number on the account
MustRemovePhoneNumber, //A phone number is already on the account
AwaitingFinalization, //Must provide an SMS code
GeneralFailure, //General failure (really now!)
AuthenticatorPresent
}
public enum FinalizeResult
{
BadSMSCode,
UnableToGenerateCorrectCodes,
Success,
GeneralFailure
}
private class AddAuthenticatorResponse
{
[JsonProperty("response")]
public SteamGuardAccount Response { get; set; }
}
private class FinalizeAuthenticatorResponse
{
[JsonProperty("response")]
public FinalizeAuthenticatorInternalResponse Response { get; set; }
internal class FinalizeAuthenticatorInternalResponse
{
[JsonProperty("status")]
public int Status { get; set; }
[JsonProperty("server_time")]
public long ServerTime { get; set; }
[JsonProperty("want_more")]
public bool WantMore { get; set; }
[JsonProperty("success")]
public bool Success { get; set; }
}
}
private class HasPhoneResponse
{
[JsonProperty("has_phone")]
public bool HasPhone { get; set; }
}
private class AddPhoneResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
}
public static string GenerateDeviceID()
{
using (var sha1 = new SHA1Managed())
{
RNGCryptoServiceProvider secureRandom = new RNGCryptoServiceProvider();
byte[] randomBytes = new byte[8];
secureRandom.GetBytes(randomBytes);
byte[] hashedBytes = sha1.ComputeHash(randomBytes);
string random32 = BitConverter.ToString(hashedBytes).Replace("-", "").Substring(0, 32).ToLower();
return "android:" + SplitOnRatios(random32, new[] { 8, 4, 4, 4, 12 }, "-");
}
}
private static string SplitOnRatios(string str, int[] ratios, string intermediate)
{
string result = "";
int pos = 0;
for (int index = 0; index < ratios.Length; index++)
{
result += str.Substring(pos, ratios[index]);
pos = ratios[index];
if (index < ratios.Length - 1)
result += intermediate;
}
return result;
}
}
}

View File

@@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SteamAuth
{
public class Confirmation
{
public string ID;
public string Key;
public string Description;
public ConfirmationType ConfType
{
get
{
if (String.IsNullOrEmpty(Description)) return ConfirmationType.Unknown;
if (Description.StartsWith("Confirm ")) return ConfirmationType.GenericConfirmation;
if (Description.StartsWith("Trade with ")) return ConfirmationType.Trade;
if (Description.StartsWith("Sell -")) return ConfirmationType.MarketSellTransaction;
return ConfirmationType.Unknown;
}
}
public enum ConfirmationType
{
GenericConfirmation,
Trade,
MarketSellTransaction,
Unknown
}
}
}

View File

@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Joshua Coffey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SteamAuth")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SteamAuth")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("5ad0934e-f6c4-4ae5-83af-c788313b2a87")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -1,40 +0,0 @@
using System.Net;
namespace SteamAuth
{
public class SessionData
{
public string SessionID { get; set; }
public string SteamLogin { get; set; }
public string SteamLoginSecure { get; set; }
public string WebCookie { get; set; }
public string OAuthToken { get; set; }
public ulong SteamID { get; set; }
public void AddCookies(CookieContainer cookies)
{
cookies.Add(new Cookie("mobileClientVersion", "0 (2.1.3)", "/", ".steamcommunity.com"));
cookies.Add(new Cookie("mobileClient", "android", "/", ".steamcommunity.com"));
cookies.Add(new Cookie("steamid", SteamID.ToString(), "/", ".steamcommunity.com"));
cookies.Add(new Cookie("steamLogin", SteamLogin, "/", ".steamcommunity.com")
{
HttpOnly = true
});
cookies.Add(new Cookie("steamLoginSecure", SteamLoginSecure, "/", ".steamcommunity.com")
{
HttpOnly = true,
Secure = true
});
cookies.Add(new Cookie("Steam_Language", "english", "/", ".steamcommunity.com"));
cookies.Add(new Cookie("dob", "", "/", ".steamcommunity.com"));
cookies.Add(new Cookie("sessionid", this.SessionID, "/", ".steamcommunity.com"));
}
}
}

View File

@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SteamAuth</RootNamespace>
<AssemblyName>SteamAuth</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>
</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1-beta1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="APIEndpoints.cs" />
<Compile Include="AuthenticatorLinker.cs" />
<Compile Include="Confirmation.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SessionData.cs" />
<Compile Include="SteamGuardAccount.cs" />
<Compile Include="SteamWeb.cs" />
<Compile Include="TimeAligner.cs" />
<Compile Include="UserLogin.cs" />
<Compile Include="Util.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -1,28 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamAuth", "SteamAuth.csproj", "{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBed", "..\TestBed\TestBed.csproj", "{8A732227-C090-4011-9F0A-51180CFE6271}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.Build.0 = Release|Any CPU
{8A732227-C090-4011-9F0A-51180CFE6271}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A732227-C090-4011-9F0A-51180CFE6271}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A732227-C090-4011-9F0A-51180CFE6271}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A732227-C090-4011-9F0A-51180CFE6271}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -1,456 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace SteamAuth
{
public class SteamGuardAccount
{
[JsonProperty("shared_secret")]
public string SharedSecret { get; set; }
[JsonProperty("serial_number")]
public string SerialNumber { get; set; }
[JsonProperty("revocation_code")]
public string RevocationCode { get; set; }
[JsonProperty("uri")]
public string URI { get; set; }
[JsonProperty("server_time")]
public long ServerTime { get; set; }
[JsonProperty("account_name")]
public string AccountName { get; set; }
[JsonProperty("token_gid")]
public string TokenGID { get; set; }
[JsonProperty("identity_secret")]
public string IdentitySecret { get; set; }
[JsonProperty("secret_1")]
public string Secret1 { get; set; }
[JsonProperty("status")]
public int Status { get; set; }
[JsonProperty("device_id")]
public string DeviceID { get; set; }
/// <summary>
/// Set to true if the authenticator has actually been applied to the account.
/// </summary>
[JsonProperty("fully_enrolled")]
public bool FullyEnrolled { get; set; }
public SessionData Session { get; set; }
private static byte[] steamGuardCodeTranslations = new byte[] { 50, 51, 52, 53, 54, 55, 56, 57, 66, 67, 68, 70, 71, 72, 74, 75, 77, 78, 80, 81, 82, 84, 86, 87, 88, 89 };
public bool DeactivateAuthenticator(int scheme = 2)
{
var postData = new NameValueCollection();
postData.Add("steamid", this.Session.SteamID.ToString());
postData.Add("steamguard_scheme", scheme.ToString());
postData.Add("revocation_code", this.RevocationCode);
postData.Add("access_token", this.Session.OAuthToken);
try
{
string response = SteamWeb.MobileLoginRequest(APIEndpoints.STEAMAPI_BASE + "/ITwoFactorService/RemoveAuthenticator/v0001", "POST", postData);
var removeResponse = JsonConvert.DeserializeObject<RemoveAuthenticatorResponse>(response);
if (removeResponse == null || removeResponse.Response == null || !removeResponse.Response.Success) return false;
return true;
}
catch (Exception)
{
return false;
}
}
public string GenerateSteamGuardCode()
{
return GenerateSteamGuardCodeForTime(TimeAligner.GetSteamTime());
}
public string GenerateSteamGuardCodeForTime(long time)
{
if (this.SharedSecret == null || this.SharedSecret.Length == 0)
{
return "";
}
byte[] sharedSecretArray = Convert.FromBase64String(this.SharedSecret);
byte[] timeArray = new byte[8];
time /= 30L;
for (int i = 8; i > 0; i--)
{
timeArray[i - 1] = (byte)time;
time >>= 8;
}
HMACSHA1 hmacGenerator = new HMACSHA1();
hmacGenerator.Key = sharedSecretArray;
byte[] hashedData = hmacGenerator.ComputeHash(timeArray);
byte[] codeArray = new byte[5];
try
{
byte b = (byte)(hashedData[19] & 0xF);
int codePoint = (hashedData[b] & 0x7F) << 24 | (hashedData[b + 1] & 0xFF) << 16 | (hashedData[b + 2] & 0xFF) << 8 | (hashedData[b + 3] & 0xFF);
for (int i = 0; i < 5; ++i)
{
codeArray[i] = steamGuardCodeTranslations[codePoint % steamGuardCodeTranslations.Length];
codePoint /= steamGuardCodeTranslations.Length;
}
}
catch (Exception)
{
return null; //Change later, catch-alls are bad!
}
return Encoding.UTF8.GetString(codeArray);
}
public Confirmation[] FetchConfirmations()
{
string url = this.GenerateConfirmationURL();
CookieContainer cookies = new CookieContainer();
this.Session.AddCookies(cookies);
string response = SteamWeb.Request(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()
{
Description = confDesc,
ID = confID,
Key = confKey
};
ret.Add(conf);
}
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()
{
Description = confDesc,
ID = confID,
Key = confKey
};
ret.Add(conf);
}
return ret.ToArray();
}
public long GetConfirmationTradeOfferID(Confirmation conf)
{
var confDetails = _getConfirmationDetails(conf);
if (confDetails == null || !confDetails.Success) return -1;
Regex tradeOfferIDRegex = new Regex("<div class=\"tradeoffer\" id=\"tradeofferid_(\\d+)\" >");
if(!tradeOfferIDRegex.IsMatch(confDetails.HTML)) return -1;
return long.Parse(tradeOfferIDRegex.Match(confDetails.HTML).Groups[1].Value);
}
public bool AcceptConfirmation(Confirmation conf)
{
return _sendConfirmationAjax(conf, "allow");
}
public bool DenyConfirmation(Confirmation conf)
{
return _sendConfirmationAjax(conf, "cancel");
}
/// <summary>
/// Refreshes the Steam session. Necessary to perform confirmations if your session has expired or changed.
/// </summary>
/// <returns></returns>
public bool RefreshSession()
{
string url = APIEndpoints.MOBILEAUTH_GETWGTOKEN;
NameValueCollection postData = new NameValueCollection();
postData.Add("access_token", this.Session.OAuthToken);
string response = SteamWeb.Request(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)
{
return false;
}
}
/// <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)
{
return false;
}
}
private ConfirmationDetailsResponse _getConfirmationDetails(Confirmation conf)
{
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/details/" + conf.ID + "?";
string queryString = GenerateConfirmationQueryParams("details");
url += queryString;
CookieContainer cookies = new CookieContainer();
this.Session.AddCookies(cookies);
string referer = GenerateConfirmationURL();
string response = SteamWeb.Request(url, "GET", null, cookies, null);
if (String.IsNullOrEmpty(response)) return null;
var confResponse = JsonConvert.DeserializeObject<ConfirmationDetailsResponse>(response);
if (confResponse == null) return null;
return confResponse;
}
private bool _sendConfirmationAjax(Confirmation conf, string op)
{
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/ajaxop";
string queryString = "?op=" + op + "&";
queryString += GenerateConfirmationQueryParams(op);
queryString += "&cid=" + conf.ID + "&ck=" + conf.Key;
url += queryString;
CookieContainer cookies = new CookieContainer();
this.Session.AddCookies(cookies);
string referer = GenerateConfirmationURL();
string response = SteamWeb.Request(url, "GET", null, cookies, null);
if (response == null) return false;
SendConfirmationResponse confResponse = JsonConvert.DeserializeObject<SendConfirmationResponse>(response);
return confResponse.Success;
}
public string GenerateConfirmationURL(string tag = "conf")
{
string endpoint = APIEndpoints.COMMUNITY_BASE + "/mobileconf/conf?";
string queryString = GenerateConfirmationQueryParams(tag);
return endpoint + queryString;
}
public string GenerateConfirmationQueryParams(string tag)
{
if (String.IsNullOrEmpty(DeviceID))
throw new ArgumentException("Device ID is not present");
long time = TimeAligner.GetSteamTime();
return "p=" + this.DeviceID + "&a=" + this.Session.SteamID.ToString() + "&k=" + _generateConfirmationHashForTime(time, tag) + "&t=" + time + "&m=android&tag=" + tag;
}
private string _generateConfirmationHashForTime(long time, string tag)
{
byte[] decode = Convert.FromBase64String(this.IdentitySecret);
int n2 = 8;
if (tag != null)
{
if (tag.Length > 32)
{
n2 = 8 + 32;
}
else
{
n2 = 8 + tag.Length;
}
}
byte[] array = new byte[n2];
int n3 = 8;
while (true)
{
int n4 = n3 - 1;
if (n3 <= 0)
{
break;
}
array[n4] = (byte)time;
time >>= 8;
n3 = n4;
}
if (tag != null)
{
Array.Copy(Encoding.UTF8.GetBytes(tag), 0, array, 8, n2 - 8);
}
try
{
HMACSHA1 hmacGenerator = new HMACSHA1();
hmacGenerator.Key = decode;
byte[] hashedData = hmacGenerator.ComputeHash(array);
string encodedData = Convert.ToBase64String(hashedData, Base64FormattingOptions.None);
string hash = WebUtility.UrlEncode(encodedData);
return hash;
}
catch (Exception)
{
return null; //Fix soon: catch-all is BAD!
}
}
//TODO: Determine how to detect an invalid session.
public class WGTokenInvalidException : Exception
{
}
private class RefreshSessionDataResponse
{
[JsonProperty("response")]
public RefreshSessionDataInternalResponse Response { get; set; }
internal class RefreshSessionDataInternalResponse
{
[JsonProperty("token")]
public string Token { get; set; }
[JsonProperty("token_secure")]
public string TokenSecure { get; set; }
}
}
private class RemoveAuthenticatorResponse
{
[JsonProperty("response")]
public RemoveAuthenticatorInternalResponse Response { get; set; }
internal class RemoveAuthenticatorInternalResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
}
}
private class SendConfirmationResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
}
private class ConfirmationDetailsResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("html")]
public string HTML { get; set; }
}
}
}

View File

@@ -1,137 +0,0 @@
using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace SteamAuth
{
public class SteamWeb
{
/// <summary>
/// Perform a mobile login request
/// </summary>
/// <param name="url">API url</param>
/// <param name="method">GET or POST</param>
/// <param name="data">Name-data pairs</param>
/// <param name="cookies">current cookie container</param>
/// <returns>response body</returns>
public static string MobileLoginRequest(string url, string method, NameValueCollection data = null, CookieContainer cookies = null, NameValueCollection headers = null)
{
return Request(url, method, data, cookies, headers, APIEndpoints.COMMUNITY_BASE + "/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client");
}
public static string Request(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
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode != HttpStatusCode.OK)
{
return null;
}
using (StreamReader responseStream = new StreamReader(response.GetResponseStream()))
{
string responseData = responseStream.ReadToEnd();
return responseData;
}
}
}
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;
}
}
}
}

View File

@@ -1,84 +0,0 @@
using System;
using System.Threading.Tasks;
using System.Net;
using Newtonsoft.Json;
namespace SteamAuth
{
/// <summary>
/// Class to help align system time with the Steam server time. Not super advanced; probably not taking some things into account that it should.
/// Necessary to generate up-to-date codes. In general, this will have an error of less than a second, assuming Steam is operational.
/// </summary>
public class TimeAligner
{
private static bool _aligned = false;
private static int _timeDifference = 0;
public static long GetSteamTime()
{
if (!TimeAligner._aligned)
{
TimeAligner.AlignTime();
}
return Util.GetSystemUnixTime() + _timeDifference;
}
public static async Task<long> GetSteamTimeAsync()
{
if (!TimeAligner._aligned)
{
await TimeAligner.AlignTimeAsync();
}
return Util.GetSystemUnixTime() + _timeDifference;
}
public static void AlignTime()
{
long currentTime = Util.GetSystemUnixTime();
using (WebClient client = new WebClient())
{
try
{
string response = client.UploadString(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;
}
}
}
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
{
[JsonProperty("response")]
internal TimeQueryResponse Response { get; set; }
internal class TimeQueryResponse
{
[JsonProperty("server_time")]
public long ServerTime { get; set; }
}
}
}
}

View File

@@ -1,252 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Specialized;
using System.Net;
using System.Security.Cryptography;
using System.Text;
namespace SteamAuth
{
/// <summary>
/// Handles logging the user into the mobile Steam website. Necessary to generate OAuth token and session cookies.
/// </summary>
public class UserLogin
{
public string Username;
public string Password;
public ulong SteamID;
public bool RequiresCaptcha;
public string CaptchaGID = null;
public string CaptchaText = null;
public bool RequiresEmail;
public string EmailDomain = null;
public string EmailCode = null;
public bool Requires2FA;
public string TwoFactorCode = null;
public SessionData Session = null;
public bool LoggedIn = false;
private CookieContainer _cookies = new CookieContainer();
public UserLogin(string username, string password)
{
this.Username = username;
this.Password = password;
}
public LoginResult DoLogin()
{
var postData = new NameValueCollection();
var cookies = _cookies;
string response = null;
if (cookies.Count == 0)
{
//Generate a SessionID
cookies.Add(new Cookie("mobileClientVersion", "0 (2.1.3)", "/", ".steamcommunity.com"));
cookies.Add(new Cookie("mobileClient", "android", "/", ".steamcommunity.com"));
cookies.Add(new Cookie("Steam_Language", "english", "/", ".steamcommunity.com"));
NameValueCollection headers = new NameValueCollection();
headers.Add("X-Requested-With", "com.valvesoftware.android.steam.community");
SteamWeb.MobileLoginRequest("https://steamcommunity.com/login?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client", "GET", null, cookies, headers);
}
postData.Add("username", this.Username);
response = SteamWeb.MobileLoginRequest(APIEndpoints.COMMUNITY_BASE + "/login/getrsakey", "POST", postData, cookies);
if (response == null || response.Contains("<BODY>\nAn error occurred while processing your request.")) return LoginResult.GeneralFailure;
var rsaResponse = JsonConvert.DeserializeObject<RSAResponse>(response);
if (!rsaResponse.Success)
{
return LoginResult.BadRSA;
}
RNGCryptoServiceProvider secureRandom = new RNGCryptoServiceProvider();
byte[] encryptedPasswordBytes;
using (var rsaEncryptor = new RSACryptoServiceProvider())
{
var passwordBytes = Encoding.ASCII.GetBytes(this.Password);
var rsaParameters = rsaEncryptor.ExportParameters(false);
rsaParameters.Exponent = Util.HexStringToByteArray(rsaResponse.Exponent);
rsaParameters.Modulus = Util.HexStringToByteArray(rsaResponse.Modulus);
rsaEncryptor.ImportParameters(rsaParameters);
encryptedPasswordBytes = rsaEncryptor.Encrypt(passwordBytes, false);
}
string encryptedPassword = Convert.ToBase64String(encryptedPasswordBytes);
postData.Clear();
postData.Add("username", this.Username);
postData.Add("password", encryptedPassword);
postData.Add("twofactorcode", this.TwoFactorCode ?? "");
postData.Add("captchagid", this.RequiresCaptcha ? this.CaptchaGID : "-1");
postData.Add("captcha_text", this.RequiresCaptcha ? this.CaptchaText : "");
postData.Add("emailsteamid", (this.Requires2FA || this.RequiresEmail) ? this.SteamID.ToString() : "");
postData.Add("emailauth", this.RequiresEmail ? this.EmailCode : "");
postData.Add("rsatimestamp", rsaResponse.Timestamp);
postData.Add("remember_login", "false");
postData.Add("oauth_client_id", "DE45CD61");
postData.Add("oauth_scope", "read_profile write_profile read_client write_client");
postData.Add("loginfriendlyname", "#login_emailauth_friendlyname_mobile");
postData.Add("donotcache", Util.GetSystemUnixTime().ToString());
response = SteamWeb.MobileLoginRequest(APIEndpoints.COMMUNITY_BASE + "/login/dologin", "POST", postData, cookies);
if (response == null) return LoginResult.GeneralFailure;
var loginResponse = JsonConvert.DeserializeObject<LoginResponse>(response);
if (loginResponse.Message != null && loginResponse.Message.Contains("Incorrect login"))
{
return LoginResult.BadCredentials;
}
if (loginResponse.CaptchaNeeded)
{
this.RequiresCaptcha = true;
this.CaptchaGID = loginResponse.CaptchaGID;
return LoginResult.NeedCaptcha;
}
if (loginResponse.EmailAuthNeeded)
{
this.RequiresEmail = true;
this.SteamID = loginResponse.EmailSteamID;
return LoginResult.NeedEmail;
}
if (loginResponse.TwoFactorNeeded && !loginResponse.Success)
{
this.Requires2FA = true;
return LoginResult.Need2FA;
}
if (loginResponse.Message != null && loginResponse.Message.Contains("too many login failures"))
{
return LoginResult.TooManyFailedLogins;
}
if (loginResponse.OAuthData == null || loginResponse.OAuthData.OAuthToken == null || loginResponse.OAuthData.OAuthToken.Length == 0)
{
return LoginResult.GeneralFailure;
}
if (!loginResponse.LoginComplete)
{
return LoginResult.BadCredentials;
}
else
{
var readableCookies = cookies.GetCookies(new Uri("https://steamcommunity.com"));
var oAuthData = loginResponse.OAuthData;
SessionData session = new SessionData();
session.OAuthToken = oAuthData.OAuthToken;
session.SteamID = oAuthData.SteamID;
session.SteamLogin = session.SteamID + "%7C%7C" + oAuthData.SteamLogin;
session.SteamLoginSecure = session.SteamID + "%7C%7C" + oAuthData.SteamLoginSecure;
session.WebCookie = oAuthData.Webcookie;
session.SessionID = readableCookies["sessionid"].Value;
this.Session = session;
this.LoggedIn = true;
return LoginResult.LoginOkay;
}
}
private class LoginResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("login_complete")]
public bool LoginComplete { get; set; }
[JsonProperty("oauth")]
public string OAuthDataString { get; set; }
public OAuth OAuthData
{
get
{
return OAuthDataString != null ? JsonConvert.DeserializeObject<OAuth>(OAuthDataString) : null;
}
}
[JsonProperty("captcha_needed")]
public bool CaptchaNeeded { get; set; }
[JsonProperty("captcha_gid")]
public string CaptchaGID { get; set; }
[JsonProperty("emailsteamid")]
public ulong EmailSteamID { get; set; }
[JsonProperty("emailauth_needed")]
public bool EmailAuthNeeded { get; set; }
[JsonProperty("requires_twofactor")]
public bool TwoFactorNeeded { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
internal class OAuth
{
[JsonProperty("steamid")]
public ulong SteamID { get; set; }
[JsonProperty("oauth_token")]
public string OAuthToken { get; set; }
[JsonProperty("wgtoken")]
public string SteamLogin { get; set; }
[JsonProperty("wgtoken_secure")]
public string SteamLoginSecure { get; set; }
[JsonProperty("webcookie")]
public string Webcookie { get; set; }
}
}
private class RSAResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("publickey_exp")]
public string Exponent { get; set; }
[JsonProperty("publickey_mod")]
public string Modulus { get; set; }
[JsonProperty("timestamp")]
public string Timestamp { get; set; }
[JsonProperty("steamid")]
public ulong SteamID { get; set; }
}
}
public enum LoginResult
{
LoginOkay,
GeneralFailure,
BadRSA,
BadCredentials,
NeedCaptcha,
Need2FA,
NeedEmail,
TooManyFailedLogins,
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.Net;
namespace SteamAuth
{
public class Util
{
public static long GetSystemUnixTime()
{
return (long)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
}
public static byte[] HexStringToByteArray(string hex)
{
int hexLen = hex.Length;
byte[] ret = new byte[hexLen / 2];
for (int i = 0; i < hexLen; i += 2)
{
ret[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return ret;
}
}
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="9.0.1-beta1" targetFramework="net451" />
</packages>

View File

@@ -6,4 +6,11 @@ clone_depth: 10
build:
project: ArchiSteamFarm.sln
parallel: true
verbosity: minimal
verbosity: minimal
notifications:
- provider: Webhook
url: https://webhooks.gitter.im/e/6cc89e76555ee263cc11
method: POST
on_build_success: true
on_build_failure: true
on_build_status_changed: true

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.