mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-25 10:46:48 +00:00
Compare commits
176 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69d6d6e10b | ||
|
|
38eb130217 | ||
|
|
c00943bb74 | ||
|
|
e134d6f6b6 | ||
|
|
79b1019ec3 | ||
|
|
4b0e65b76b | ||
|
|
49c4f94bba | ||
|
|
58cc203577 | ||
|
|
6128d87cbf | ||
|
|
6ada40fc2f | ||
|
|
f8ab06871f | ||
|
|
066c1358db | ||
|
|
0f3b7becdf | ||
|
|
dcda639cb1 | ||
|
|
27a555dde6 | ||
|
|
0e699eb95b | ||
|
|
17dc0e3911 | ||
|
|
cd462f607e | ||
|
|
4e6723a7e2 | ||
|
|
9cc7e30c98 | ||
|
|
cbf212aff5 | ||
|
|
ab4630c191 | ||
|
|
e1e68d2d6a | ||
|
|
b594af5c44 | ||
|
|
d243bf260f | ||
|
|
af956f22e9 | ||
|
|
aac72d4c5b | ||
|
|
dc98d794b8 | ||
|
|
831856cf15 | ||
|
|
686c0aaf8b | ||
|
|
9eadf805b5 | ||
|
|
3b8315073b | ||
|
|
bd028ba459 | ||
|
|
49344751af | ||
|
|
97724c95a7 | ||
|
|
627c4576f5 | ||
|
|
a90b5adaf5 | ||
|
|
1c74fddbac | ||
|
|
4380679df6 | ||
|
|
e3030dccdb | ||
|
|
e6509ae1a3 | ||
|
|
2bc202dd3a | ||
|
|
b9e8823bf1 | ||
|
|
e7cd488435 | ||
|
|
4c09d95b9a | ||
|
|
1fe5cfff49 | ||
|
|
57014aab6d | ||
|
|
4a5bff1b84 | ||
|
|
5f0ce543ae | ||
|
|
25094592c9 | ||
|
|
8f75042b54 | ||
|
|
34c1016218 | ||
|
|
7351d07518 | ||
|
|
5a8701444a | ||
|
|
8db29fa5a7 | ||
|
|
056b793262 | ||
|
|
a4383cdb89 | ||
|
|
d5514422b6 | ||
|
|
3627a01f59 | ||
|
|
0dc131b79b | ||
|
|
8d9fbce2ed | ||
|
|
a01e718e28 | ||
|
|
9bed5f013a | ||
|
|
2cf2df62c5 | ||
|
|
9bac3915da | ||
|
|
93191f9066 | ||
|
|
ebc8c1674b | ||
|
|
5bc6af718e | ||
|
|
579d9e1cbf | ||
|
|
c7d0fb1aac | ||
|
|
1f4a4cc6b7 | ||
|
|
612ab87626 | ||
|
|
951d58161f | ||
|
|
d68bb01174 | ||
|
|
da411b81b9 | ||
|
|
82c2d3ec15 | ||
|
|
788f5c94a2 | ||
|
|
4d37a775cf | ||
|
|
766a638f0d | ||
|
|
12aa933355 | ||
|
|
9bc76ca1fe | ||
|
|
339a56dc80 | ||
|
|
d16228b2a5 | ||
|
|
99dbca1b36 | ||
|
|
943bc7e3d9 | ||
|
|
9535479602 | ||
|
|
90ade53ae7 | ||
|
|
9a51386b7e | ||
|
|
03ee96057f | ||
|
|
a23bca7960 | ||
|
|
6b4ae6a4d7 | ||
|
|
5c80fd158d | ||
|
|
885800c539 | ||
|
|
920d4b9ed6 | ||
|
|
2d02bd609e | ||
|
|
e658ae33b1 | ||
|
|
379018866b | ||
|
|
1ad1e8b792 | ||
|
|
c3dde4c822 | ||
|
|
b40dc2e572 | ||
|
|
70bdd34d66 | ||
|
|
56a6e10189 | ||
|
|
41f8a0a474 | ||
|
|
92f347e28b | ||
|
|
1a1914540c | ||
|
|
449e4f9511 | ||
|
|
959bf98039 | ||
|
|
017c5eb7ef | ||
|
|
9e575584a8 | ||
|
|
19da8c6d11 | ||
|
|
3d19a69c60 | ||
|
|
f6a8d16c85 | ||
|
|
f13991c2da | ||
|
|
40a3d6558d | ||
|
|
fea76a3dda | ||
|
|
1087c01a2c | ||
|
|
fd49ff5483 | ||
|
|
0f5d9a665c | ||
|
|
ad63432645 | ||
|
|
f36681ea18 | ||
|
|
ce94035d98 | ||
|
|
7583e50cf3 | ||
|
|
687de60476 | ||
|
|
e181eb354b | ||
|
|
4e5ddefac9 | ||
|
|
e588ba3d2c | ||
|
|
6d087a9ac9 | ||
|
|
529d366b6c | ||
|
|
4657d00d11 | ||
|
|
3a5edab651 | ||
|
|
d662d9dd6a | ||
|
|
e508e99d14 | ||
|
|
fdc705e955 | ||
|
|
38f48841bd | ||
|
|
2ebce59ee7 | ||
|
|
adefa6446d | ||
|
|
f18c3b301e | ||
|
|
b79265e74a | ||
|
|
50853d8d7e | ||
|
|
7e084bf50b | ||
|
|
60a02a4c6a | ||
|
|
fcfdbdd220 | ||
|
|
ac10f32431 | ||
|
|
5d7e0290d7 | ||
|
|
f170f16919 | ||
|
|
96d9ea6056 | ||
|
|
d8bf424ac3 | ||
|
|
95d2860afd | ||
|
|
a75ed7047b | ||
|
|
d627570cc9 | ||
|
|
4e22d7fcd1 | ||
|
|
bb05f4c67a | ||
|
|
238838b0fd | ||
|
|
ae8413b72b | ||
|
|
027e301420 | ||
|
|
1a6c5a3cff | ||
|
|
378a87bc86 | ||
|
|
27635d260b | ||
|
|
8f99620598 | ||
|
|
bb285512d1 | ||
|
|
88690d8c09 | ||
|
|
0ff442e3e1 | ||
|
|
fedc3268b6 | ||
|
|
fc0d0abaaf | ||
|
|
95df9057c2 | ||
|
|
ed6e35da85 | ||
|
|
64f424e474 | ||
|
|
7b67755932 | ||
|
|
a5f7e7988c | ||
|
|
80ed0e66bb | ||
|
|
5529a8e1f0 | ||
|
|
496bea5ac5 | ||
|
|
52f3a86255 | ||
|
|
7d205cfa42 | ||
|
|
c4f47c56da | ||
|
|
546440d9dc |
64
.gitignore
vendored
64
.gitignore
vendored
@@ -15,37 +15,6 @@ ArchiSteamFarm/debug/*
|
||||
# Ignore out
|
||||
out/
|
||||
|
||||
#################
|
||||
## Eclipse
|
||||
#################
|
||||
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
|
||||
#################
|
||||
## Visual Studio
|
||||
#################
|
||||
@@ -63,7 +32,6 @@ local.properties
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
@@ -199,35 +167,3 @@ $RECYCLE.BIN/
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
|
||||
|
||||
#############
|
||||
## Python
|
||||
#############
|
||||
|
||||
*.py[cod]
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist/
|
||||
build/
|
||||
eggs/
|
||||
parts/
|
||||
var/
|
||||
sdist/
|
||||
develop-eggs/
|
||||
.installed.cfg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
|
||||
#Translations
|
||||
*.mo
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/EMPTY_BLOCK_STYLE/@EntryValue">TOGETHER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHING_EMPTY_BRACES/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AES/@EntryIndexedValue">AES</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=API/@EntryIndexedValue">API</s:String>
|
||||
<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>
|
||||
@@ -14,4 +20,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WTF/@EntryIndexedValue">WTF</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XML/@EntryIndexedValue">XML</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="_" Suffix="" Style="AaBb" /><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy></s:String></wpf:ResourceDictionary>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="_" Suffix="" Style="AaBb" /><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
@@ -85,9 +89,7 @@ namespace ArchiSteamFarm {
|
||||
JobID = jobID;
|
||||
|
||||
if (msg.count_new_items > 0) {
|
||||
Notifications = new HashSet<ENotification> {
|
||||
ENotification.Items
|
||||
};
|
||||
Notifications = new HashSet<ENotification> { ENotification.Items };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
52
ArchiSteamFarm/ArchiServiceInstaller.cs
Normal file
52
ArchiSteamFarm/ArchiServiceInstaller.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
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.ComponentModel;
|
||||
using System.Configuration.Install;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.ServiceProcess;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
[RunInstaller(true)]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public sealed class ArchiServiceInstaller : Installer {
|
||||
public ArchiServiceInstaller() {
|
||||
ServiceInstaller serviceInstaller = new ServiceInstaller();
|
||||
ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
|
||||
|
||||
serviceInstaller.ServiceName = SharedInfo.ServiceName;
|
||||
serviceInstaller.DisplayName = SharedInfo.ServiceName;
|
||||
serviceInstaller.Description = SharedInfo.ServiceDescription;
|
||||
|
||||
// Defaulting to only starting when a user starts it, can be easily changed after install
|
||||
serviceInstaller.StartType = ServiceStartMode.Manual;
|
||||
|
||||
// System account, requires admin privilege to install
|
||||
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
|
||||
|
||||
Installers.Add(serviceInstaller);
|
||||
Installers.Add(serviceProcessInstaller);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 />
|
||||
@@ -27,6 +27,8 @@
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -71,12 +73,16 @@
|
||||
<DelaySign>false</DelaySign>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="HtmlAgilityPack, Version=1.4.9.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll</HintPath>
|
||||
<Reference Include="HtmlAgilityPack, Version=1.4.9.4, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\HtmlAgilityPack.1.4.9.4\lib\Net45\HtmlAgilityPack.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<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>
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.0-beta13\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="protobuf-net, Version=2.0.0.668, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
|
||||
@@ -88,19 +94,31 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ArchiHandler.cs" />
|
||||
<Compile Include="ArchiWebHandler.cs" />
|
||||
<Compile Include="ArchiServiceInstaller.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Bot.cs" />
|
||||
<Compile Include="BotConfig.cs" />
|
||||
<Compile Include="ConcurrentEnumerator.cs" />
|
||||
<Compile Include="ConcurrentHashSet.cs" />
|
||||
<Compile Include="CryptoHelper.cs" />
|
||||
<Compile Include="GlobalDatabase.cs" />
|
||||
<Compile Include="BotDatabase.cs" />
|
||||
<Compile Include="CardsFarmer.cs" />
|
||||
@@ -109,15 +127,21 @@
|
||||
<Compile Include="JSON\GitHub.cs" />
|
||||
<Compile Include="JSON\Steam.cs" />
|
||||
<Compile Include="Logging.cs" />
|
||||
<Compile Include="MobileAuthenticator.cs" />
|
||||
<Compile Include="Runtime.cs" />
|
||||
<Compile Include="ObsoleteSteamGuardAccount.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SharedInfo.cs" />
|
||||
<Compile Include="Trading.cs" />
|
||||
<Compile Include="Utilities.cs" />
|
||||
<Compile Include="WCF.cs" />
|
||||
<Compile Include="WebBrowser.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="config\ASF.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
@@ -143,12 +167,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="cirno.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SteamAuth\SteamAuth.csproj">
|
||||
<Project>{5ad0934e-f6c4-4ae5-83af-c788313b2a87}</Project>
|
||||
<Name>SteamAuth</Name>
|
||||
</ProjectReference>
|
||||
<None Include="FodyWeavers.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
@@ -162,18 +181,29 @@
|
||||
copy "$(TargetDir)config\ASF.json" "$(SolutionDir)out\config"
|
||||
copy "$(TargetDir)config\example.json" "$(SolutionDir)out\config"
|
||||
copy "$(TargetDir)config\minimal.json" "$(SolutionDir)out\config"
|
||||
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
del "$(SolutionDir)out\ASF.exe.config"
|
||||
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-Service.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
del "$(SolutionDir)out\ASF-Service.exe.config"
|
||||
copy "$(TargetDir)$(TargetName).exe" "$(SolutionDir)out\ASF.exe"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mkdir -p "$(SolutionDir)out/config"
|
||||
cp "$(TargetDir)config/ASF.json" "$(SolutionDir)out/config"
|
||||
cp "$(TargetDir)config/example.json" "$(SolutionDir)out/config"
|
||||
cp "$(TargetDir)config/minimal.json" "$(SolutionDir)out/config"
|
||||
mono --llvm --server -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
rm "$(SolutionDir)out/ASF.exe.config"
|
||||
mono --llvm --server -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-Service.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
rm "$(SolutionDir)out/ASF-Service.exe.config"
|
||||
cp "$(SolutionDir)out/ASF-Service.exe" "$(SolutionDir)out/ASF.exe"
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Fody.1.30.0-beta01\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.30.0-beta01\build\dotnet\Fody.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Costura.Fody.2.0.0-beta0018\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.2.0.0-beta0018\build\Costura.Fody.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Fody.1.30.0-beta01\build\dotnet\Fody.targets" Condition="'$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' AND Exists('..\packages\Fody.1.30.0-beta01\build\dotnet\Fody.targets')" />
|
||||
<Import Project="..\packages\Costura.Fody.2.0.0-beta0018\build\Costura.Fody.targets" Condition="'$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' AND Exists('..\packages\Costura.Fody.2.0.0-beta0018\build\Costura.Fody.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">
|
||||
|
||||
@@ -47,6 +47,9 @@ namespace ArchiSteamFarm {
|
||||
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
|
||||
private readonly WebBrowser WebBrowser;
|
||||
|
||||
internal bool Ready { get; private set; }
|
||||
|
||||
private ulong SteamID;
|
||||
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
|
||||
|
||||
internal static void Init() {
|
||||
@@ -61,18 +64,12 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
int index = hashName.IndexOf('-');
|
||||
if (index < 1) {
|
||||
Logging.LogNullError(nameof(index));
|
||||
if (index <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint appID;
|
||||
if (uint.TryParse(hashName.Substring(0, index), out appID)) {
|
||||
return appID;
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(appID));
|
||||
return 0;
|
||||
return uint.TryParse(hashName.Substring(0, index), out appID) ? appID : 0;
|
||||
}
|
||||
|
||||
private static Steam.Item.EType GetItemType(string name) {
|
||||
@@ -107,6 +104,53 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ParseItems(Dictionary<ulong, Tuple<uint, Steam.Item.EType>> descriptions, List<KeyValue> input, HashSet<Steam.Item> output) {
|
||||
if ((descriptions == null) || (input == null) || (input.Count == 0) || (output == null)) {
|
||||
Logging.LogNullError(nameof(descriptions) + " || " + nameof(input) + " || " + nameof(output));
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (KeyValue item in input) {
|
||||
uint appID = (uint) item["appid"].AsUnsignedLong();
|
||||
if (appID == 0) {
|
||||
Logging.LogNullError(nameof(appID));
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong contextID = item["contextid"].AsUnsignedLong();
|
||||
if (contextID == 0) {
|
||||
Logging.LogNullError(nameof(contextID));
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong classID = item["classid"].AsUnsignedLong();
|
||||
if (classID == 0) {
|
||||
Logging.LogNullError(nameof(classID));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint amount = (uint) item["amount"].AsUnsignedLong();
|
||||
if (amount == 0) {
|
||||
Logging.LogNullError(nameof(amount));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint realAppID = 0;
|
||||
Steam.Item.EType type = Steam.Item.EType.Unknown;
|
||||
|
||||
Tuple<uint, Steam.Item.EType> description;
|
||||
if (descriptions.TryGetValue(classID, out description)) {
|
||||
realAppID = description.Item1;
|
||||
type = description.Item2;
|
||||
}
|
||||
|
||||
Steam.Item steamItem = new Steam.Item(appID, contextID, classID, amount, realAppID, type);
|
||||
output.Add(steamItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal ArchiWebHandler(Bot bot) {
|
||||
if (bot == null) {
|
||||
throw new ArgumentNullException(nameof(bot));
|
||||
@@ -117,25 +161,24 @@ namespace ArchiSteamFarm {
|
||||
WebBrowser = new WebBrowser(bot.BotName);
|
||||
}
|
||||
|
||||
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
|
||||
if ((steamClient == null) || string.IsNullOrEmpty(webAPIUserNonce)) {
|
||||
Logging.LogNullError(nameof(steamClient) + " || " + nameof(webAPIUserNonce), Bot.BotName);
|
||||
internal void OnDisconnected() => Ready = false;
|
||||
|
||||
internal async Task<bool> Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalPin) {
|
||||
if ((steamID == 0) || (universe == EUniverse.Invalid) || string.IsNullOrEmpty(webAPIUserNonce)) {
|
||||
Logging.LogNullError(nameof(steamID) + " || " + nameof(universe) + " || " + nameof(webAPIUserNonce), Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong steamID = steamClient.SteamID;
|
||||
if (steamID == 0) {
|
||||
return false;
|
||||
}
|
||||
SteamID = steamID;
|
||||
|
||||
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
|
||||
|
||||
// Generate an AES session key
|
||||
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
|
||||
byte[] sessionKey = SteamKit2.CryptoHelper.GenerateRandomBlock(32);
|
||||
|
||||
// RSA encrypt it with the public key for the universe we're on
|
||||
byte[] cryptedSessionKey;
|
||||
using (RSACrypto rsa = new RSACrypto(KeyDictionary.GetPublicKey(steamClient.ConnectedUniverse))) {
|
||||
using (RSACrypto rsa = new RSACrypto(KeyDictionary.GetPublicKey(universe))) {
|
||||
cryptedSessionKey = rsa.Encrypt(sessionKey);
|
||||
}
|
||||
|
||||
@@ -144,7 +187,7 @@ namespace ArchiSteamFarm {
|
||||
Array.Copy(Encoding.ASCII.GetBytes(webAPIUserNonce), loginKey, webAPIUserNonce.Length);
|
||||
|
||||
// AES encrypt the loginkey with our session key
|
||||
byte[] cryptedLoginKey = CryptoHelper.SymmetricEncrypt(loginKey, sessionKey);
|
||||
byte[] cryptedLoginKey = SteamKit2.CryptoHelper.SymmetricEncrypt(loginKey, sessionKey);
|
||||
|
||||
// Do the magic
|
||||
Logging.LogGenericInfo("Logging in to ISteamUserAuth...", Bot.BotName);
|
||||
@@ -182,10 +225,12 @@ namespace ArchiSteamFarm {
|
||||
string steamLoginSecure = authResult["tokensecure"].Value;
|
||||
WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunityHost));
|
||||
|
||||
if (!UnlockParentalAccount(parentalPin).Result) {
|
||||
// Unlock Steam Parental if needed
|
||||
if (!await UnlockParentalAccount(parentalPin).ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Ready = true;
|
||||
LastSessionRefreshCheck = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
@@ -239,6 +284,89 @@ 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), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/mobileconf/conf?l=english&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;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
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) {
|
||||
Logging.LogNullError(nameof(response), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
response.ConfirmationID = confirmationID;
|
||||
return response;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
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;
|
||||
@@ -261,19 +389,19 @@ namespace ArchiSteamFarm {
|
||||
XmlNode appNode = xmlNode.SelectSingleNode("appID");
|
||||
if (appNode == null) {
|
||||
Logging.LogNullError(nameof(appNode), Bot.BotName);
|
||||
continue;
|
||||
return null;
|
||||
}
|
||||
|
||||
uint appID;
|
||||
if (!uint.TryParse(appNode.InnerText, out appID)) {
|
||||
Logging.LogNullError(nameof(appID), Bot.BotName);
|
||||
continue;
|
||||
return null;
|
||||
}
|
||||
|
||||
XmlNode nameNode = xmlNode.SelectSingleNode("name");
|
||||
if (nameNode == null) {
|
||||
Logging.LogNullError(nameof(nameNode), Bot.BotName);
|
||||
continue;
|
||||
return null;
|
||||
}
|
||||
|
||||
result[appID] = nameNode.InnerText;
|
||||
@@ -284,17 +412,15 @@ 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;
|
||||
}
|
||||
|
||||
KeyValue response = null;
|
||||
using (dynamic iPlayerService = WebAPI.GetInterface("IPlayerService", Bot.BotConfig.SteamApiKey)) {
|
||||
iPlayerService.Timeout = Timeout;
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
using (dynamic iPlayerService = WebAPI.GetInterface("IPlayerService", Bot.BotConfig.SteamApiKey)) {
|
||||
iPlayerService.Timeout = Timeout;
|
||||
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
try {
|
||||
response = iPlayerService.GetOwnedGames(
|
||||
steamid: steamID,
|
||||
@@ -308,7 +434,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
Logging.LogGenericWarning("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -316,8 +442,8 @@ namespace ArchiSteamFarm {
|
||||
foreach (KeyValue game in response["games"].Children) {
|
||||
uint appID = (uint) game["appid"].AsUnsignedLong();
|
||||
if (appID == 0) {
|
||||
Logging.LogNullError(nameof(appID));
|
||||
continue;
|
||||
Logging.LogNullError(nameof(appID), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
result[appID] = game["name"].Value;
|
||||
@@ -326,19 +452,96 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
internal HashSet<Steam.TradeOffer> GetTradeOffers() {
|
||||
internal uint GetServerTime() {
|
||||
KeyValue response = null;
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
using (dynamic iTwoFactorService = WebAPI.GetInterface("ITwoFactorService")) {
|
||||
iTwoFactorService.Timeout = Timeout;
|
||||
|
||||
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.LogGenericWarning("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;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/tradeoffer/" + tradeID + "?l=english";
|
||||
|
||||
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) { // Trade can be no longer valid
|
||||
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> GetActiveTradeOffers() {
|
||||
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;
|
||||
}
|
||||
|
||||
KeyValue response = null;
|
||||
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
|
||||
iEconService.Timeout = Timeout;
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
|
||||
iEconService.Timeout = Timeout;
|
||||
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
try {
|
||||
response = iEconService.GetTradeOffers(
|
||||
get_received_offers: 1,
|
||||
@@ -353,7 +556,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
Logging.LogGenericWarning("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -362,7 +565,7 @@ namespace ArchiSteamFarm {
|
||||
ulong classID = description["classid"].AsUnsignedLong();
|
||||
if (classID == 0) {
|
||||
Logging.LogNullError(nameof(classID), Bot.BotName);
|
||||
continue;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (descriptions.ContainsKey(classID)) {
|
||||
@@ -370,13 +573,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);
|
||||
@@ -387,48 +595,44 @@ namespace ArchiSteamFarm {
|
||||
|
||||
HashSet<Steam.TradeOffer> result = new HashSet<Steam.TradeOffer>();
|
||||
foreach (KeyValue trade in response["trade_offers_received"].Children) {
|
||||
Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
|
||||
TradeOfferID = trade["tradeofferid"].AsUnsignedLong(),
|
||||
OtherSteamID3 = (uint) trade["accountid_other"].AsUnsignedLong(),
|
||||
State = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
|
||||
};
|
||||
|
||||
foreach (KeyValue item in trade["items_to_give"].Children) {
|
||||
Steam.Item steamItem = new Steam.Item {
|
||||
AppID = (uint) item["appid"].AsUnsignedLong(),
|
||||
ContextID = item["contextid"].AsUnsignedLong(),
|
||||
AssetID = item["assetid"].AsUnsignedLong(),
|
||||
ClassID = item["classid"].AsUnsignedLong(),
|
||||
InstanceID = item["instanceid"].AsUnsignedLong(),
|
||||
Amount = (uint) item["amount"].AsUnsignedLong()
|
||||
};
|
||||
|
||||
Tuple<uint, Steam.Item.EType> description;
|
||||
if (descriptions.TryGetValue(steamItem.ClassID, out description)) {
|
||||
steamItem.RealAppID = description.Item1;
|
||||
steamItem.Type = description.Item2;
|
||||
}
|
||||
|
||||
tradeOffer.ItemsToGive.Add(steamItem);
|
||||
Steam.TradeOffer.ETradeOfferState state = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>();
|
||||
if (state == Steam.TradeOffer.ETradeOfferState.Unknown) {
|
||||
Logging.LogNullError(nameof(state));
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (KeyValue item in trade["items_to_receive"].Children) {
|
||||
Steam.Item steamItem = new Steam.Item {
|
||||
AppID = (uint) item["appid"].AsUnsignedLong(),
|
||||
ContextID = item["contextid"].AsUnsignedLong(),
|
||||
AssetID = item["assetid"].AsUnsignedLong(),
|
||||
ClassID = item["classid"].AsUnsignedLong(),
|
||||
InstanceID = item["instanceid"].AsUnsignedLong(),
|
||||
Amount = (uint) item["amount"].AsUnsignedLong()
|
||||
};
|
||||
if (state != Steam.TradeOffer.ETradeOfferState.Active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Tuple<uint, Steam.Item.EType> description;
|
||||
if (descriptions.TryGetValue(steamItem.ClassID, out description)) {
|
||||
steamItem.RealAppID = description.Item1;
|
||||
steamItem.Type = description.Item2;
|
||||
ulong tradeOfferID = trade["tradeofferid"].AsUnsignedLong();
|
||||
if (tradeOfferID == 0) {
|
||||
Logging.LogNullError(nameof(tradeOfferID));
|
||||
return null;
|
||||
}
|
||||
|
||||
uint otherSteamID3 = (uint) trade["accountid_other"].AsUnsignedLong();
|
||||
if (otherSteamID3 == 0) {
|
||||
Logging.LogNullError(nameof(otherSteamID3));
|
||||
return null;
|
||||
}
|
||||
|
||||
Steam.TradeOffer tradeOffer = new Steam.TradeOffer(tradeOfferID, otherSteamID3, state);
|
||||
|
||||
List<KeyValue> itemsToGive = trade["items_to_give"].Children;
|
||||
if (itemsToGive.Count > 0) {
|
||||
if (!ParseItems(descriptions, itemsToGive, tradeOffer.ItemsToGive)) {
|
||||
Logging.LogGenericError("Parsing " + nameof(itemsToGive) + " failed!", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
tradeOffer.ItemsToReceive.Add(steamItem);
|
||||
List<KeyValue> itemsToReceive = trade["items_to_receive"].Children;
|
||||
if (itemsToReceive.Count > 0) {
|
||||
if (!ParseItems(descriptions, itemsToReceive, tradeOffer.ItemsToReceive)) {
|
||||
Logging.LogGenericError("Parsing " + nameof(itemsToReceive) + " failed!", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
result.Add(tradeOffer);
|
||||
@@ -466,17 +670,15 @@ 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;
|
||||
}
|
||||
|
||||
KeyValue response = null;
|
||||
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
|
||||
iEconService.Timeout = Timeout;
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
|
||||
iEconService.Timeout = Timeout;
|
||||
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
try {
|
||||
response = iEconService.DeclineTradeOffer(
|
||||
tradeofferid: tradeID.ToString(),
|
||||
@@ -489,24 +691,24 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
if (response != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
Logging.LogGenericWarning("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
internal async Task<HashSet<Steam.Item>> GetMyTradableInventory() {
|
||||
internal async Task<HashSet<Steam.Item>> GetMyInventory(bool tradable) {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HashSet<Steam.Item> result = new HashSet<Steam.Item>();
|
||||
|
||||
ushort nextPage = 0;
|
||||
uint currentPage = 0;
|
||||
while (true) {
|
||||
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1&start=" + nextPage;
|
||||
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=" + (tradable ? "1" : "0") + "&start=" + currentPage;
|
||||
|
||||
JObject jObject = await WebBrowser.UrlGetToJObjectRetry(request).ConfigureAwait(false);
|
||||
if (jObject == null) {
|
||||
@@ -523,13 +725,13 @@ namespace ArchiSteamFarm {
|
||||
string classIDString = description["classid"].ToString();
|
||||
if (string.IsNullOrEmpty(classIDString)) {
|
||||
Logging.LogNullError(nameof(classIDString), Bot.BotName);
|
||||
continue;
|
||||
return null;
|
||||
}
|
||||
|
||||
ulong classID;
|
||||
if (!ulong.TryParse(classIDString, out classID) || (classID == 0)) {
|
||||
Logging.LogNullError(nameof(classID), Bot.BotName);
|
||||
continue;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (descriptionMap.ContainsKey(classID)) {
|
||||
@@ -537,13 +739,27 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
uint appID = 0;
|
||||
Steam.Item.EType type = Steam.Item.EType.Unknown;
|
||||
|
||||
string hashName = description["market_hash_name"].ToString();
|
||||
if (!string.IsNullOrEmpty(hashName)) {
|
||||
appID = GetAppIDFromMarketHashName(hashName);
|
||||
}
|
||||
|
||||
if (appID == 0) {
|
||||
string appIDString = description["appid"].ToString();
|
||||
if (string.IsNullOrEmpty(appIDString)) {
|
||||
Logging.LogNullError(nameof(appIDString), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!uint.TryParse(appIDString, out appID)) {
|
||||
Logging.LogNullError(nameof(appID), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Steam.Item.EType type = Steam.Item.EType.Unknown;
|
||||
|
||||
string descriptionType = description["type"].ToString();
|
||||
if (!string.IsNullOrEmpty(descriptionType)) {
|
||||
type = GetItemType(descriptionType);
|
||||
@@ -559,19 +775,18 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
foreach (JToken item in items) {
|
||||
|
||||
Steam.Item steamItem;
|
||||
|
||||
try {
|
||||
steamItem = JsonConvert.DeserializeObject<Steam.Item>(item.ToString());
|
||||
steamItem = item.ToObject<Steam.Item>();
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
continue;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (steamItem == null) {
|
||||
Logging.LogNullError(nameof(steamItem), Bot.BotName);
|
||||
continue;
|
||||
return null;
|
||||
}
|
||||
|
||||
Tuple<uint, Steam.Item.EType> description;
|
||||
@@ -588,12 +803,13 @@ namespace ArchiSteamFarm {
|
||||
break; // OK, last page
|
||||
}
|
||||
|
||||
if (ushort.TryParse(jObject["more_start"].ToString(), out nextPage)) {
|
||||
continue;
|
||||
uint nextPage;
|
||||
if (!uint.TryParse(jObject["more_start"].ToString(), out nextPage) || (nextPage <= currentPage)) {
|
||||
Logging.LogNullError(nameof(nextPage), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(nextPage), Bot.BotName);
|
||||
break;
|
||||
currentPage = nextPage;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -630,13 +846,7 @@ namespace ArchiSteamFarm {
|
||||
itemID = 0;
|
||||
}
|
||||
|
||||
singleTrade.ItemsToGive.Assets.Add(new Steam.Item {
|
||||
AppID = Steam.Item.SteamAppID,
|
||||
ContextID = Steam.Item.SteamContextID,
|
||||
Amount = item.Amount,
|
||||
AssetID = item.AssetID
|
||||
});
|
||||
|
||||
singleTrade.ItemsToGive.Assets.Add(new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamContextID, item.AssetID, item.Amount));
|
||||
itemID++;
|
||||
}
|
||||
|
||||
@@ -668,8 +878,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/my/badges?p=" + page;
|
||||
|
||||
string request = SteamCommunityURL + "/my/badges?l=english&p=" + page;
|
||||
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -683,8 +892,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/my/gamecards/" + appID;
|
||||
|
||||
string request = SteamCommunityURL + "/my/gamecards/" + appID + "?l=english";
|
||||
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -694,7 +902,6 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/my/inventory";
|
||||
|
||||
return await WebBrowser.UrlHeadRetry(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,11 +25,13 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
// ReSharper disable once ClassCannotBeInstantiated
|
||||
// ReSharper disable once ClassNeverInstantiated.Global
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
internal sealed class BotConfig {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool Enabled { get; private set; } = false;
|
||||
@@ -43,6 +45,10 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty]
|
||||
internal string SteamPassword { get; set; }
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
[SuppressMessage("ReSharper", "ConvertToConstant.Local")]
|
||||
private readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;
|
||||
|
||||
[JsonProperty]
|
||||
internal string SteamParentalPIN { get; set; } = "0";
|
||||
|
||||
@@ -82,9 +88,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;
|
||||
|
||||
@@ -126,6 +129,21 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Support encrypted passwords
|
||||
if ((botConfig.PasswordFormat != CryptoHelper.ECryptoMethod.PlainText) && !string.IsNullOrEmpty(botConfig.SteamPassword)) {
|
||||
// In worst case password will result in null, which will have to be corrected by user during runtime
|
||||
botConfig.SteamPassword = CryptoHelper.Decrypt(botConfig.PasswordFormat, botConfig.SteamPassword);
|
||||
}
|
||||
|
||||
// User might not know what he's doing
|
||||
// Ensure that he can't screw core ASF variables
|
||||
if (botConfig.GamesPlayedWhileIdle.Count <= CardsFarmer.MaxGamesPlayedConcurrently) {
|
||||
return botConfig;
|
||||
}
|
||||
|
||||
Logging.LogGenericWarning("Playing more than " + CardsFarmer.MaxGamesPlayedConcurrently + " games concurrently is not possible, only first " + CardsFarmer.MaxGamesPlayedConcurrently + " entries from GamesPlayedWhileIdle will be used");
|
||||
botConfig.GamesPlayedWhileIdle = new HashSet<uint>(botConfig.GamesPlayedWhileIdle.Take(CardsFarmer.MaxGamesPlayedConcurrently));
|
||||
|
||||
return botConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,13 +23,16 @@
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using SteamAuth;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class BotDatabase {
|
||||
[JsonProperty]
|
||||
private string _LoginKey;
|
||||
|
||||
internal string LoginKey {
|
||||
get {
|
||||
return _LoginKey;
|
||||
@@ -44,7 +47,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 +82,6 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
private string _LoginKey;
|
||||
|
||||
[JsonProperty]
|
||||
private SteamGuardAccount _SteamGuardAccount;
|
||||
|
||||
private string FilePath;
|
||||
|
||||
internal static BotDatabase Load(string filePath) {
|
||||
@@ -109,11 +127,22 @@ namespace ArchiSteamFarm {
|
||||
private BotDatabase() { }
|
||||
|
||||
internal void Save() {
|
||||
string json = JsonConvert.SerializeObject(this);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
Logging.LogNullError(nameof(json));
|
||||
return;
|
||||
}
|
||||
|
||||
lock (FilePath) {
|
||||
try {
|
||||
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
for (byte i = 0; i < 5; i++) {
|
||||
try {
|
||||
File.WriteAllText(FilePath, json);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,16 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class CardsFarmer {
|
||||
internal const byte MaxGamesPlayedConcurrently = 32; // This is limit introduced by Steam Network
|
||||
|
||||
[JsonProperty]
|
||||
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
|
||||
|
||||
[JsonProperty]
|
||||
internal readonly ConcurrentHashSet<uint> CurrentGamesFarming = new ConcurrentHashSet<uint>();
|
||||
|
||||
private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
|
||||
@@ -42,9 +48,10 @@ namespace ArchiSteamFarm {
|
||||
private readonly Bot Bot;
|
||||
private readonly Timer Timer;
|
||||
|
||||
[JsonProperty]
|
||||
internal bool ManualMode { get; private set; }
|
||||
|
||||
private bool NowFarming;
|
||||
private bool KeepFarming, NowFarming;
|
||||
|
||||
internal CardsFarmer(Bot bot) {
|
||||
if (bot == null) {
|
||||
@@ -55,7 +62,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
if ((Timer == null) && (Program.GlobalConfig.IdleFarmingPeriod > 0)) {
|
||||
Timer = new Timer(
|
||||
async e => await CheckGamesForFarming().ConfigureAwait(false),
|
||||
e => CheckGamesForFarming(),
|
||||
null,
|
||||
TimeSpan.FromHours(Program.GlobalConfig.IdleFarmingPeriod), // Delay
|
||||
TimeSpan.FromHours(Program.GlobalConfig.IdleFarmingPeriod) // Period
|
||||
@@ -102,12 +109,12 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// This is the last moment for final check if we can farm
|
||||
if (Bot.PlayingBlocked) {
|
||||
Logging.LogGenericInfo("But account is currently occupied, so farming is stopped!");
|
||||
Logging.LogGenericInfo("But account is currently occupied, so farming is stopped!", Bot.BotName);
|
||||
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
||||
return;
|
||||
}
|
||||
|
||||
NowFarming = true;
|
||||
KeepFarming = NowFarming = true;
|
||||
FarmingSemaphore.Release(); // From this point we allow other calls to shut us down
|
||||
|
||||
do {
|
||||
@@ -170,26 +177,46 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Sending signal to stop farming", Bot.BotName);
|
||||
KeepFarming = false;
|
||||
FarmResetEvent.Set();
|
||||
|
||||
Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName);
|
||||
for (byte i = 0; (i < 5) && NowFarming; i++) {
|
||||
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (NowFarming) {
|
||||
Logging.LogGenericWarning("Timed out!", Bot.BotName);
|
||||
}
|
||||
|
||||
FarmResetEvent.Reset();
|
||||
Logging.LogGenericInfo("Farming stopped!", Bot.BotName);
|
||||
Bot.OnFarmingStopped();
|
||||
FarmingSemaphore.Release();
|
||||
}
|
||||
|
||||
internal async Task RestartFarming() {
|
||||
await StopFarming().ConfigureAwait(false);
|
||||
await StartFarming().ConfigureAwait(false);
|
||||
internal void OnDisconnected() => StopFarming().Forget();
|
||||
|
||||
internal void OnNewItemsNotification() {
|
||||
if (!NowFarming) {
|
||||
return;
|
||||
}
|
||||
|
||||
FarmResetEvent.Set();
|
||||
}
|
||||
|
||||
internal async Task OnNewGameAdded() {
|
||||
if (!NowFarming) {
|
||||
// If we're not farming yet, obviously it's worth it to make a check
|
||||
StartFarming().Forget();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Bot.BotConfig.CardDropsRestricted && (GamesToFarm.Count > 0) && (GamesToFarm.Values.Min() < 2)) {
|
||||
// If we have Complex algorithm and some games to boost, it's also worth to make a check
|
||||
// That's because we would check for new games after our current round anyway
|
||||
await StopFarming().ConfigureAwait(false);
|
||||
StartFarming().Forget();
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<uint> GetGamesToFarmSolo(ConcurrentDictionary<uint, float> gamesToFarm) {
|
||||
@@ -218,22 +245,25 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
byte maxPages = 1;
|
||||
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
|
||||
if ((htmlNodeCollection != null) && (htmlNodeCollection.Count > 0)) {
|
||||
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
|
||||
|
||||
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("(//a[@class='pagelink'])[last()]");
|
||||
if (htmlNode != null) {
|
||||
string lastPage = htmlNode.InnerText;
|
||||
if (!string.IsNullOrEmpty(lastPage)) {
|
||||
if (!byte.TryParse(lastPage, out maxPages)) {
|
||||
maxPages = 1; // Should never happen
|
||||
}
|
||||
if (string.IsNullOrEmpty(lastPage)) {
|
||||
Logging.LogNullError(nameof(lastPage), Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!byte.TryParse(lastPage, out maxPages) || (maxPages == 0)) {
|
||||
Logging.LogNullError(nameof(maxPages), Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GamesToFarm.Clear();
|
||||
|
||||
CheckPage(htmlDocument);
|
||||
|
||||
if (maxPages <= 1) {
|
||||
if (maxPages == 1) {
|
||||
return GamesToFarm.Count > 0;
|
||||
}
|
||||
|
||||
@@ -269,19 +299,19 @@ namespace ArchiSteamFarm {
|
||||
string steamLink = farmingNode.GetAttributeValue("href", null);
|
||||
if (string.IsNullOrEmpty(steamLink)) {
|
||||
Logging.LogNullError(nameof(steamLink), Bot.BotName);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
int index = steamLink.LastIndexOf('/');
|
||||
if (index < 0) {
|
||||
Logging.LogNullError(nameof(index), Bot.BotName);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (steamLink.Length <= index) {
|
||||
Logging.LogNullError(nameof(steamLink.Length), Bot.BotName);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
steamLink = steamLink.Substring(index);
|
||||
@@ -289,7 +319,7 @@ namespace ArchiSteamFarm {
|
||||
uint appID;
|
||||
if (!uint.TryParse(steamLink, out appID) || (appID == 0)) {
|
||||
Logging.LogNullError(nameof(appID), Bot.BotName);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (GlobalConfig.GlobalBlacklist.Contains(appID) || Program.GlobalConfig.Blacklist.Contains(appID)) {
|
||||
@@ -299,13 +329,13 @@ namespace ArchiSteamFarm {
|
||||
HtmlNode timeNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']");
|
||||
if (timeNode == null) {
|
||||
Logging.LogNullError(nameof(timeNode), Bot.BotName);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
string hoursString = timeNode.InnerText;
|
||||
if (string.IsNullOrEmpty(hoursString)) {
|
||||
Logging.LogNullError(nameof(hoursString), Bot.BotName);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
float hours = 0;
|
||||
@@ -314,7 +344,7 @@ namespace ArchiSteamFarm {
|
||||
if (match.Success) {
|
||||
if (!float.TryParse(match.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out hours)) {
|
||||
Logging.LogNullError(nameof(hours), Bot.BotName);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,12 +366,12 @@ namespace ArchiSteamFarm {
|
||||
CheckPage(htmlDocument);
|
||||
}
|
||||
|
||||
private async Task CheckGamesForFarming() {
|
||||
private void CheckGamesForFarming() {
|
||||
if (NowFarming || ManualMode || !Bot.SteamClient.IsConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
await StartFarming().ConfigureAwait(false);
|
||||
StartFarming().Forget();
|
||||
}
|
||||
|
||||
private async Task<bool?> ShouldFarm(uint appID) {
|
||||
@@ -356,12 +386,29 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||
if (htmlNode != null) {
|
||||
return !htmlNode.InnerText.Contains("No card drops");
|
||||
if (htmlNode == null) {
|
||||
Logging.LogNullError(nameof(htmlNode), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(htmlNode), Bot.BotName);
|
||||
return null;
|
||||
string progress = htmlNode.InnerText;
|
||||
if (string.IsNullOrEmpty(progress)) {
|
||||
Logging.LogNullError(nameof(progress), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte cardsRemaining = 0;
|
||||
|
||||
Match match = Regex.Match(progress, @"\d+");
|
||||
if (match.Success) {
|
||||
if (!byte.TryParse(match.Value, out cardsRemaining)) {
|
||||
Logging.LogNullError(nameof(cardsRemaining), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Status for " + appID + ": " + cardsRemaining + " cards remaining", Bot.BotName);
|
||||
return cardsRemaining > 0;
|
||||
}
|
||||
|
||||
private bool FarmMultiple() {
|
||||
@@ -375,6 +422,10 @@ namespace ArchiSteamFarm {
|
||||
if (game.Value > maxHour) {
|
||||
maxHour = game.Value;
|
||||
}
|
||||
|
||||
if (CurrentGamesFarming.Count >= MaxGamesPlayedConcurrently) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxHour >= 2) {
|
||||
@@ -423,22 +474,28 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Bot.ArchiHandler.PlayGames(appID);
|
||||
DateTime endFarmingDate = DateTime.Now.AddHours(Program.GlobalConfig.MaxFarmingTime);
|
||||
|
||||
bool success = true;
|
||||
|
||||
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||
for (ushort farmingTime = 0; (farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime) && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
|
||||
|
||||
while (keepFarming.GetValueOrDefault(true) && (DateTime.Now < endFarmingDate)) {
|
||||
Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName);
|
||||
|
||||
DateTime startFarmingPeriod = DateTime.Now;
|
||||
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||
success = false;
|
||||
break;
|
||||
FarmResetEvent.Reset();
|
||||
success = KeepFarming;
|
||||
}
|
||||
|
||||
// Don't forget to update our GamesToFarm hours
|
||||
float timePlayed = Program.GlobalConfig.FarmingDelay / 60.0F;
|
||||
GamesToFarm[appID] += timePlayed;
|
||||
GamesToFarm[appID] += (float) DateTime.Now.Subtract(startFarmingPeriod).TotalHours;
|
||||
|
||||
if (!success) {
|
||||
break;
|
||||
}
|
||||
|
||||
keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||
Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName);
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Stopped farming: " + appID, Bot.BotName);
|
||||
@@ -451,27 +508,29 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxHour >= 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bot.ArchiHandler.PlayGames(appIDs);
|
||||
|
||||
bool success = true;
|
||||
while (maxHour < 2) {
|
||||
Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName);
|
||||
|
||||
DateTime startFarmingPeriod = DateTime.Now;
|
||||
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||
success = false;
|
||||
break;
|
||||
FarmResetEvent.Reset();
|
||||
success = KeepFarming;
|
||||
}
|
||||
|
||||
// Don't forget to update our GamesToFarm hours
|
||||
float timePlayed = Program.GlobalConfig.FarmingDelay / 60.0F;
|
||||
float timePlayed = (float) DateTime.Now.Subtract(startFarmingPeriod).TotalHours;
|
||||
foreach (uint appID in appIDs) {
|
||||
GamesToFarm[appID] += timePlayed;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
break;
|
||||
}
|
||||
|
||||
maxHour += timePlayed;
|
||||
Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName);
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Stopped farming: " + string.Join(", ", appIDs), Bot.BotName);
|
||||
|
||||
@@ -36,14 +36,14 @@ namespace ArchiSteamFarm {
|
||||
private readonly IEnumerator<T> Enumerator;
|
||||
private readonly ReaderWriterLockSlim Lock;
|
||||
|
||||
internal ConcurrentEnumerator(ICollection<T> collection, ReaderWriterLockSlim @lock) {
|
||||
if ((collection == null) || (@lock == null)) {
|
||||
throw new ArgumentNullException(nameof(collection) + " || " + nameof(@lock));
|
||||
internal ConcurrentEnumerator(ICollection<T> collection, ReaderWriterLockSlim rwLock) {
|
||||
if ((collection == null) || (rwLock == null)) {
|
||||
throw new ArgumentNullException(nameof(collection) + " || " + nameof(rwLock));
|
||||
}
|
||||
|
||||
@lock.EnterReadLock();
|
||||
rwLock.EnterReadLock();
|
||||
|
||||
Lock = @lock;
|
||||
Lock = rwLock;
|
||||
Enumerator = collection.GetEnumerator();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ using System.Threading;
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ConcurrentHashSet<T> : ICollection<T>, IDisposable {
|
||||
private readonly HashSet<T> HashSet = new HashSet<T>();
|
||||
private readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
private readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
public IEnumerator<T> GetEnumerator() => new ConcurrentEnumerator<T>(HashSet, Lock);
|
||||
|
||||
166
ArchiSteamFarm/CryptoHelper.cs
Normal file
166
ArchiSteamFarm/CryptoHelper.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
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.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class CryptoHelper {
|
||||
internal enum ECryptoMethod : byte {
|
||||
PlainText,
|
||||
AES,
|
||||
ProtectedDataForCurrentUser
|
||||
}
|
||||
|
||||
private static byte[] EncryptionKey = Encoding.UTF8.GetBytes("ArchiSteamFarm");
|
||||
|
||||
internal static void SetEncryptionKey(string key) {
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
Logging.LogNullError(nameof(key));
|
||||
return;
|
||||
}
|
||||
|
||||
EncryptionKey = Encoding.UTF8.GetBytes(key);
|
||||
}
|
||||
|
||||
internal static string Encrypt(ECryptoMethod cryptoMethod, string decrypted) {
|
||||
if (string.IsNullOrEmpty(decrypted)) {
|
||||
Logging.LogNullError(nameof(decrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (cryptoMethod) {
|
||||
case ECryptoMethod.PlainText:
|
||||
return decrypted;
|
||||
case ECryptoMethod.AES:
|
||||
return EncryptAES(decrypted);
|
||||
case ECryptoMethod.ProtectedDataForCurrentUser:
|
||||
return EncryptProtectedDataForCurrentUser(decrypted);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Decrypt(ECryptoMethod cryptoMethod, string encrypted) {
|
||||
if (string.IsNullOrEmpty(encrypted)) {
|
||||
Logging.LogNullError(nameof(encrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (cryptoMethod) {
|
||||
case ECryptoMethod.PlainText:
|
||||
return encrypted;
|
||||
case ECryptoMethod.AES:
|
||||
return DecryptAES(encrypted);
|
||||
case ECryptoMethod.ProtectedDataForCurrentUser:
|
||||
return DecryptProtectedDataForCurrentUser(encrypted);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string EncryptAES(string decrypted) {
|
||||
if (string.IsNullOrEmpty(decrypted)) {
|
||||
Logging.LogNullError(nameof(decrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] key;
|
||||
using (SHA256Managed sha256 = new SHA256Managed()) {
|
||||
key = sha256.ComputeHash(EncryptionKey);
|
||||
}
|
||||
|
||||
byte[] encryptedData = Encoding.UTF8.GetBytes(decrypted);
|
||||
encryptedData = SteamKit2.CryptoHelper.SymmetricEncrypt(encryptedData, key);
|
||||
return Convert.ToBase64String(encryptedData);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string DecryptAES(string encrypted) {
|
||||
if (string.IsNullOrEmpty(encrypted)) {
|
||||
Logging.LogNullError(nameof(encrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] key;
|
||||
using (SHA256Managed sha256 = new SHA256Managed()) {
|
||||
key = sha256.ComputeHash(EncryptionKey);
|
||||
}
|
||||
|
||||
byte[] decryptedData = Convert.FromBase64String(encrypted);
|
||||
decryptedData = SteamKit2.CryptoHelper.SymmetricDecrypt(decryptedData, key);
|
||||
return Encoding.UTF8.GetString(decryptedData);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string EncryptProtectedDataForCurrentUser(string decrypted) {
|
||||
if (string.IsNullOrEmpty(decrypted)) {
|
||||
Logging.LogNullError(nameof(decrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] encryptedData = ProtectedData.Protect(
|
||||
Encoding.UTF8.GetBytes(decrypted),
|
||||
EncryptionKey, // This is used as salt only
|
||||
DataProtectionScope.CurrentUser
|
||||
);
|
||||
|
||||
return Convert.ToBase64String(encryptedData);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string DecryptProtectedDataForCurrentUser(string encrypted) {
|
||||
if (string.IsNullOrEmpty(encrypted)) {
|
||||
Logging.LogNullError(nameof(encrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] decryptedData = ProtectedData.Unprotect(
|
||||
Convert.FromBase64String(encrypted),
|
||||
EncryptionKey, // This is used as salt only
|
||||
DataProtectionScope.CurrentUser
|
||||
);
|
||||
|
||||
return Encoding.UTF8.GetString(decryptedData);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
4
ArchiSteamFarm/FodyWeavers.xml
Normal file
4
ArchiSteamFarm/FodyWeavers.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers>
|
||||
<Costura IncludeDebugSymbols='false' />
|
||||
</Weavers>
|
||||
@@ -30,7 +30,8 @@ using System.IO;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated"), SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
internal sealed class GlobalConfig {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal enum EUpdateChannel : byte {
|
||||
@@ -42,12 +43,12 @@ namespace ArchiSteamFarm {
|
||||
internal const byte DefaultHttpTimeout = 60;
|
||||
|
||||
private const byte DefaultMaxFarmingTime = 10;
|
||||
private const byte DefaultFarmingDelay = 5;
|
||||
private const byte DefaultFarmingDelay = 15;
|
||||
private const ushort DefaultWCFPort = 1242;
|
||||
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
|
||||
|
||||
// This is hardcoded blacklist which should not be possible to change
|
||||
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280 };
|
||||
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280, 480730 };
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool Debug { get; private set; } = false;
|
||||
@@ -80,11 +81,17 @@ namespace ArchiSteamFarm {
|
||||
internal byte FarmingDelay { get; private set; } = DefaultFarmingDelay;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte LoginLimiterDelay { get; private set; } = 7;
|
||||
internal byte LoginLimiterDelay { get; private set; } = 10;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte InventoryLimiterDelay { get; private set; } = 3;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte GiftsLimiterDelay { get; private set; } = 1;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte MaxTradeHoldDuration { get; private set; } = 15;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool ForceHttp { get; private set; } = false;
|
||||
|
||||
@@ -158,6 +165,11 @@ namespace ArchiSteamFarm {
|
||||
globalConfig.FarmingDelay = DefaultFarmingDelay;
|
||||
}
|
||||
|
||||
if ((globalConfig.FarmingDelay > 5) && Runtime.RequiresWorkaroundForMonoBug41701()) {
|
||||
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;
|
||||
|
||||
@@ -26,6 +26,7 @@ using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class GlobalDatabase {
|
||||
@@ -91,11 +92,22 @@ namespace ArchiSteamFarm {
|
||||
private GlobalDatabase() { }
|
||||
|
||||
private void Save() {
|
||||
string json = JsonConvert.SerializeObject(this);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
Logging.LogNullError(nameof(json));
|
||||
return;
|
||||
}
|
||||
|
||||
lock (FilePath) {
|
||||
try {
|
||||
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
for (byte i = 0; i < 5; i++) {
|
||||
try {
|
||||
File.WriteAllText(FilePath, json);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -22,15 +22,19 @@
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using HtmlAgilityPack;
|
||||
using Newtonsoft.Json;
|
||||
using SteamKit2;
|
||||
|
||||
namespace ArchiSteamFarm.JSON {
|
||||
internal static class Steam {
|
||||
internal sealed class Item { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
|
||||
// Deserialized from JSON (SteamCommunity) and constructed from code
|
||||
internal const ushort SteamAppID = 753;
|
||||
internal const byte SteamContextID = 6;
|
||||
|
||||
@@ -48,9 +52,10 @@ namespace ArchiSteamFarm.JSON {
|
||||
TradingCard
|
||||
}
|
||||
|
||||
internal uint AppID { get; set; }
|
||||
internal uint AppID { get; private 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();
|
||||
@@ -58,21 +63,24 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
Logging.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
uint result;
|
||||
if (!uint.TryParse(value, out result)) {
|
||||
uint appID;
|
||||
if (!uint.TryParse(value, out appID) || (appID == 0)) {
|
||||
Logging.LogNullError(nameof(appID));
|
||||
return;
|
||||
}
|
||||
|
||||
AppID = result;
|
||||
AppID = appID;
|
||||
}
|
||||
}
|
||||
|
||||
internal ulong ContextID { get; set; }
|
||||
internal ulong ContextID { get; private 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();
|
||||
@@ -80,19 +88,21 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
Logging.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
ulong contextID;
|
||||
if (!ulong.TryParse(value, out contextID) || (contextID == 0)) {
|
||||
Logging.LogNullError(nameof(contextID));
|
||||
return;
|
||||
}
|
||||
|
||||
ContextID = result;
|
||||
ContextID = contextID;
|
||||
}
|
||||
}
|
||||
|
||||
internal ulong AssetID { get; set; }
|
||||
internal ulong AssetID { get; private set; }
|
||||
|
||||
[JsonProperty(PropertyName = "assetid", Required = Required.DisallowNull)]
|
||||
private string AssetIDString {
|
||||
@@ -102,27 +112,31 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
Logging.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
ulong assetID;
|
||||
if (!ulong.TryParse(value, out assetID) || (assetID == 0)) {
|
||||
Logging.LogNullError(nameof(assetID));
|
||||
return;
|
||||
}
|
||||
|
||||
AssetID = result;
|
||||
AssetID = assetID;
|
||||
}
|
||||
}
|
||||
|
||||
[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; }
|
||||
}
|
||||
|
||||
internal ulong ClassID { get; set; }
|
||||
internal ulong ClassID { get; private 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();
|
||||
@@ -130,43 +144,23 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
Logging.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
ulong classID;
|
||||
if (!ulong.TryParse(value, out classID) || (classID == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClassID = result;
|
||||
ClassID = classID;
|
||||
}
|
||||
}
|
||||
|
||||
internal ulong InstanceID { get; set; }
|
||||
internal uint Amount { get; private set; }
|
||||
|
||||
[JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
private string InstanceIDString {
|
||||
get {
|
||||
return InstanceID.ToString();
|
||||
}
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
InstanceID = result;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -174,23 +168,59 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
Logging.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
uint result;
|
||||
if (!uint.TryParse(value, out result)) {
|
||||
uint amount;
|
||||
if (!uint.TryParse(value, out amount) || (amount == 0)) {
|
||||
Logging.LogNullError(nameof(amount));
|
||||
return;
|
||||
}
|
||||
|
||||
Amount = result;
|
||||
Amount = amount;
|
||||
}
|
||||
}
|
||||
|
||||
internal uint RealAppID { get; set; }
|
||||
internal EType Type { get; set; }
|
||||
|
||||
// This constructor is used for constructing items in trades being sent
|
||||
internal Item(uint appID, ulong contextID, ulong assetID, uint amount) : this(appID, contextID, amount) {
|
||||
if (assetID == 0) {
|
||||
throw new ArgumentNullException(nameof(assetID));
|
||||
}
|
||||
|
||||
AssetID = assetID;
|
||||
}
|
||||
|
||||
// This constructor is used for constructing items in trades being received
|
||||
internal Item(uint appID, ulong contextID, ulong classID, uint amount, uint realAppID, EType type) : this(appID, contextID, amount) {
|
||||
if (classID == 0) {
|
||||
throw new ArgumentNullException(nameof(classID));
|
||||
}
|
||||
|
||||
ClassID = classID;
|
||||
RealAppID = realAppID;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
private Item() { }
|
||||
|
||||
private Item(uint appID, ulong contextID, uint amount) {
|
||||
if ((appID == 0) || (contextID == 0) || (amount == 0)) {
|
||||
throw new ArgumentNullException(nameof(appID) + " || " + nameof(contextID) + " || " + nameof(amount));
|
||||
}
|
||||
|
||||
AppID = appID;
|
||||
ContextID = contextID;
|
||||
Amount = amount;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TradeOffer { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
|
||||
// Constructed from code
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal enum ETradeOfferState : byte {
|
||||
Unknown,
|
||||
@@ -207,44 +237,41 @@ namespace ArchiSteamFarm.JSON {
|
||||
OnHold
|
||||
}
|
||||
|
||||
internal ulong TradeOfferID { get; set; }
|
||||
internal readonly ulong TradeOfferID;
|
||||
internal readonly ETradeOfferState State;
|
||||
internal readonly HashSet<Item> ItemsToGive = new HashSet<Item>();
|
||||
internal readonly HashSet<Item> ItemsToReceive = new HashSet<Item>();
|
||||
|
||||
[JsonProperty(PropertyName = "tradeofferid", Required = Required.Always), SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
private string TradeOfferIDString {
|
||||
private readonly uint OtherSteamID3;
|
||||
|
||||
private ulong _OtherSteamID64;
|
||||
internal ulong OtherSteamID64 {
|
||||
get {
|
||||
return TradeOfferID.ToString();
|
||||
}
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
return;
|
||||
if (_OtherSteamID64 != 0) {
|
||||
return _OtherSteamID64;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
return;
|
||||
if (OtherSteamID3 == 0) {
|
||||
Logging.LogNullError(nameof(OtherSteamID3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
TradeOfferID = result;
|
||||
_OtherSteamID64 = new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
|
||||
return _OtherSteamID64;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "accountid_other", Required = Required.Always)]
|
||||
internal uint OtherSteamID3 { private get; set; }
|
||||
internal TradeOffer(ulong tradeOfferID, uint otherSteamID3, ETradeOfferState state) {
|
||||
if ((tradeOfferID == 0) || (otherSteamID3 == 0) || (state == ETradeOfferState.Unknown)) {
|
||||
throw new ArgumentNullException(nameof(tradeOfferID) + " || " + nameof(otherSteamID3) + " || " + nameof(state));
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "trade_offer_state", Required = Required.Always)]
|
||||
internal ETradeOfferState State { get; set; }
|
||||
TradeOfferID = tradeOfferID;
|
||||
OtherSteamID3 = otherSteamID3;
|
||||
State = state;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "items_to_give", Required = Required.Always)]
|
||||
internal HashSet<Item> ItemsToGive { get; } = new HashSet<Item>();
|
||||
|
||||
[JsonProperty(PropertyName = "items_to_receive", Required = Required.Always)]
|
||||
internal HashSet<Item> ItemsToReceive { get; } = new HashSet<Item>();
|
||||
|
||||
// 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>>();
|
||||
@@ -304,22 +331,203 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal sealed class TradeOfferRequest {
|
||||
// Constructed from code
|
||||
internal sealed class ItemList {
|
||||
[JsonProperty(PropertyName = "assets", Required = Required.Always)]
|
||||
internal HashSet<Item> Assets { get; } = new HashSet<Item>();
|
||||
internal readonly HashSet<Item> Assets = new HashSet<Item>();
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "newversion", Required = Required.Always)]
|
||||
internal bool NewVersion { get; } = true;
|
||||
|
||||
[JsonProperty(PropertyName = "version", Required = Required.Always)]
|
||||
internal byte Version { get; } = 2;
|
||||
|
||||
[JsonProperty(PropertyName = "me", Required = Required.Always)]
|
||||
internal ItemList ItemsToGive { get; } = new ItemList();
|
||||
internal readonly ItemList ItemsToGive = new ItemList();
|
||||
|
||||
[JsonProperty(PropertyName = "them", Required = Required.Always)]
|
||||
internal ItemList ItemsToReceive { get; } = new ItemList();
|
||||
internal readonly ItemList ItemsToReceive = new ItemList();
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
|
||||
internal sealed class ConfirmationResponse {
|
||||
// Deserialized from JSON
|
||||
[JsonProperty(PropertyName = "success", Required = Required.Always)]
|
||||
internal bool Success { get; private set; }
|
||||
|
||||
private ConfirmationResponse() { }
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
|
||||
internal sealed class ConfirmationDetails {
|
||||
// Deserialized from JSON
|
||||
internal enum EType : byte {
|
||||
Unknown,
|
||||
Trade,
|
||||
Market,
|
||||
Other
|
||||
}
|
||||
|
||||
private uint _ConfirmationID;
|
||||
internal uint ConfirmationID {
|
||||
get { return _ConfirmationID; }
|
||||
|
||||
set {
|
||||
if (value == 0) {
|
||||
Logging.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
_ConfirmationID = value;
|
||||
}
|
||||
}
|
||||
|
||||
[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) {
|
||||
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) || (HtmlDocument == null)) {
|
||||
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) || (OtherSteamID3 == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_OtherSteamID64 = new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
|
||||
return _OtherSteamID64;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(PropertyName = "html")]
|
||||
private string HTML;
|
||||
#pragma warning restore 649
|
||||
|
||||
private uint _OtherSteamID3;
|
||||
private uint OtherSteamID3 {
|
||||
get {
|
||||
if (_OtherSteamID3 != 0) {
|
||||
return _OtherSteamID3;
|
||||
}
|
||||
|
||||
if ((Type != EType.Trade) || (HtmlDocument == null)) {
|
||||
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)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_HtmlDocument = new HtmlDocument();
|
||||
_HtmlDocument.LoadHtml(WebUtility.HtmlDecode(HTML));
|
||||
return _HtmlDocument;
|
||||
}
|
||||
}
|
||||
|
||||
private ConfirmationDetails() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,148 +23,165 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Targets;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Logging {
|
||||
private static readonly object FileLock = new object();
|
||||
private const string Layout = @"${date:format=yyyy-MM-dd HH\:mm\:ss}|${level:uppercase=true}|${message}${onexception:inner= ${exception:format=toString,Data}}";
|
||||
|
||||
private static bool LogToFile;
|
||||
private static readonly HashSet<LoggingRule> ConsoleLoggingRules = new HashSet<LoggingRule>();
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
internal static void Init() {
|
||||
LogToFile = Program.GlobalConfig.LogToFile;
|
||||
private static bool IsUsingCustomConfiguration;
|
||||
|
||||
if (!LogToFile) {
|
||||
internal static void InitCoreLoggers() {
|
||||
if (LogManager.Configuration != null) {
|
||||
// User provided custom NLog config, or we have it set already, so don't override it
|
||||
IsUsingCustomConfiguration = true;
|
||||
InitConsoleLoggers();
|
||||
return;
|
||||
}
|
||||
|
||||
lock (FileLock) {
|
||||
if (!LogToFile) {
|
||||
return;
|
||||
}
|
||||
LoggingConfiguration config = new LoggingConfiguration();
|
||||
|
||||
try {
|
||||
File.Delete(Program.LogFile);
|
||||
} catch (Exception e) {
|
||||
LogToFile = false;
|
||||
LogGenericException(e);
|
||||
}
|
||||
}
|
||||
ColoredConsoleTarget consoleTarget = new ColoredConsoleTarget("Console") {
|
||||
Layout = Layout
|
||||
};
|
||||
|
||||
config.AddTarget(consoleTarget);
|
||||
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, consoleTarget));
|
||||
|
||||
LogManager.Configuration = config;
|
||||
InitConsoleLoggers();
|
||||
}
|
||||
|
||||
internal static void LogGenericWTF(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||
internal static void InitEnhancedLoggers() {
|
||||
if (IsUsingCustomConfiguration) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Program.GlobalConfig.LogToFile) {
|
||||
FileTarget fileTarget = new FileTarget("File") {
|
||||
DeleteOldFileOnStartup = true,
|
||||
FileName = Program.LogFile,
|
||||
Layout = Layout
|
||||
};
|
||||
|
||||
LogManager.Configuration.AddTarget(fileTarget);
|
||||
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, fileTarget));
|
||||
}
|
||||
|
||||
if (Program.IsRunningAsService) {
|
||||
EventLogTarget eventLogTarget = new EventLogTarget("EventLog") {
|
||||
Layout = Layout
|
||||
};
|
||||
|
||||
LogManager.Configuration.AddTarget(eventLogTarget);
|
||||
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, eventLogTarget));
|
||||
}
|
||||
|
||||
LogGenericInfo("Logging module initialized!");
|
||||
}
|
||||
|
||||
internal static void OnUserInputStart() {
|
||||
if (ConsoleLoggingRules.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (LoggingRule consoleLoggingRule in ConsoleLoggingRules) {
|
||||
LogManager.Configuration.LoggingRules.Remove(consoleLoggingRule);
|
||||
}
|
||||
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
|
||||
internal static void OnUserInputEnd() {
|
||||
if (ConsoleLoggingRules.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (LoggingRule consoleLoggingRule in ConsoleLoggingRules) {
|
||||
LogManager.Configuration.LoggingRules.Add(consoleLoggingRule);
|
||||
}
|
||||
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
|
||||
internal static void LogGenericError(string message, string botName = Program.ASF, [CallerMemberName] string previousMethodName = null) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
LogNullError(nameof(message), botName);
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[!!] WTF: " + previousMethodName + "() <" + botName + "> " + message + ", WTF?");
|
||||
Logger.Error($"{botName}|{previousMethodName}() {message}");
|
||||
}
|
||||
|
||||
internal static void LogGenericError(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||
internal static void LogGenericException(Exception exception, string botName = Program.ASF, [CallerMemberName] string previousMethodName = null) {
|
||||
if (exception == null) {
|
||||
LogNullError(nameof(exception), botName);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Error(exception, $"{botName}|{previousMethodName}()");
|
||||
}
|
||||
|
||||
internal static void LogFatalException(Exception exception, string botName = Program.ASF, [CallerMemberName] string previousMethodName = null) {
|
||||
if (exception == null) {
|
||||
LogNullError(nameof(exception), botName);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Fatal(exception, $"{botName}|{previousMethodName}()");
|
||||
}
|
||||
|
||||
internal static void LogGenericWarning(string message, string botName = Program.ASF, [CallerMemberName] string previousMethodName = null) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
LogNullError(nameof(message), botName);
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
|
||||
Logger.Warn($"{botName}|{previousMethodName}() {message}");
|
||||
}
|
||||
|
||||
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||
while (true) {
|
||||
if (exception == null) {
|
||||
LogNullError(nameof(exception), botName);
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
|
||||
Log("[!] StackTrace:" + Environment.NewLine + exception.StackTrace);
|
||||
|
||||
if (exception.InnerException != null) {
|
||||
exception = exception.InnerException;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LogGenericWarning(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||
internal static void LogGenericInfo(string message, string botName = Program.ASF, [CallerMemberName] string previousMethodName = null) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
LogNullError(nameof(message), botName);
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[!] WARNING: " + previousMethodName + "() <" + botName + "> " + message);
|
||||
}
|
||||
|
||||
internal static void LogGenericInfo(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
LogNullError(nameof(message), botName);
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[*] INFO: " + previousMethodName + "() <" + botName + "> " + message);
|
||||
Logger.Info($"{botName}|{previousMethodName}() {message}");
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ExplicitCallerInfoArgument")]
|
||||
internal static void LogNullError(string nullObjectName, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||
while (true) {
|
||||
if (string.IsNullOrEmpty(nullObjectName)) {
|
||||
nullObjectName = nameof(nullObjectName);
|
||||
continue;
|
||||
}
|
||||
|
||||
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
|
||||
break;
|
||||
internal static void LogNullError(string nullObjectName, string botName = Program.ASF, [CallerMemberName] string previousMethodName = null) {
|
||||
if (string.IsNullOrEmpty(nullObjectName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG"), SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||
[Conditional("DEBUG")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal static void LogGenericDebug(string message, string botName = Program.ASF, [CallerMemberName] string previousMethodName = null) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
LogNullError(nameof(message), botName);
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[#] DEBUG: " + previousMethodName + "() <" + botName + "> " + message);
|
||||
Logger.Debug($"{botName}|{previousMethodName}() {message}");
|
||||
}
|
||||
|
||||
private static void Log(string message) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
LogNullError(nameof(message));
|
||||
return;
|
||||
}
|
||||
|
||||
string loggedMessage = DateTime.Now + " " + message + Environment.NewLine;
|
||||
|
||||
// Write on console only when not awaiting response from user
|
||||
if (!Program.ConsoleIsBusy) {
|
||||
try {
|
||||
Console.Write(loggedMessage);
|
||||
} catch {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
if (!LogToFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
lock (FileLock) {
|
||||
if (!LogToFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
File.AppendAllText(Program.LogFile, loggedMessage);
|
||||
} catch (Exception e) {
|
||||
LogToFile = false;
|
||||
LogGenericException(e);
|
||||
}
|
||||
private static void InitConsoleLoggers() {
|
||||
foreach (LoggingRule loggingRule in from loggingRule in LogManager.Configuration.LoggingRules from target in loggingRule.Targets where target is ColoredConsoleTarget || target is ConsoleTarget select loggingRule) {
|
||||
ConsoleLoggingRules.Add(loggingRule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
318
ArchiSteamFarm/MobileAuthenticator.cs
Normal file
318
ArchiSteamFarm/MobileAuthenticator.cs
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
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.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).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
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)) {
|
||||
Logging.LogNullError(nameof(confirmationHash), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
Steam.ConfirmationDetails response = await Bot.ArchiWebHandler.GetConfirmationDetails(DeviceID, confirmationHash, time, confirmation.ID).ConfigureAwait(false);
|
||||
if ((response == null) || !response.Success) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
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).ConfigureAwait(false);
|
||||
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);
|
||||
return null;
|
||||
}
|
||||
|
||||
uint id;
|
||||
if (!uint.TryParse(idString, out id) || (id == 0)) {
|
||||
Logging.LogNullError(nameof(id), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
string keyString = confirmationNode.GetAttributeValue("data-key", null);
|
||||
if (string.IsNullOrEmpty(keyString)) {
|
||||
Logging.LogNullError(nameof(keyString), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
ulong key;
|
||||
if (!ulong.TryParse(keyString, out key) || (key == 0)) {
|
||||
Logging.LogNullError(nameof(key), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
HtmlNode descriptionNode = confirmationNode.SelectSingleNode(".//div[@class='mobileconf_list_entry_description']/div");
|
||||
if (descriptionNode == null) {
|
||||
Logging.LogNullError(nameof(descriptionNode), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
ArchiSteamFarm/ObsoleteSteamGuardAccount.cs
Normal file
49
ArchiSteamFarm/ObsoleteSteamGuardAccount.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
// TODO: This will be completely removed soon
|
||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
|
||||
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; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.JSON;
|
||||
@@ -58,13 +59,12 @@ namespace ArchiSteamFarm {
|
||||
Server // Normal + WCF server
|
||||
}
|
||||
|
||||
internal const string ASF = "ASF";
|
||||
internal const string ConfigDirectory = "config";
|
||||
internal const string DebugDirectory = "debug";
|
||||
internal const string LogFile = "log.txt";
|
||||
internal const string GithubRepo = "JustArchi/ArchiSteamFarm";
|
||||
|
||||
private const string ASF = "ASF";
|
||||
private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only
|
||||
private const string GithubReleaseURL = "https://api.github.com/repos/" + SharedInfo.GithubRepo + "/releases"; // GitHub API is HTTPS only
|
||||
private const string GlobalConfigFile = ASF + ".json";
|
||||
private const string GlobalDatabaseFile = ASF + ".db";
|
||||
|
||||
@@ -77,9 +77,9 @@ namespace ArchiSteamFarm {
|
||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
|
||||
private static readonly WCF WCF = new WCF();
|
||||
|
||||
internal static bool IsRunningAsService { get; private set; }
|
||||
internal static GlobalConfig GlobalConfig { get; private set; }
|
||||
internal static GlobalDatabase GlobalDatabase { get; private set; }
|
||||
internal static bool ConsoleIsBusy { get; private set; }
|
||||
|
||||
private static Timer AutoUpdatesTimer;
|
||||
private static EMode Mode = EMode.Normal;
|
||||
@@ -91,7 +91,7 @@ namespace ArchiSteamFarm {
|
||||
// We booted successfully so we can now remove old exe file
|
||||
if (File.Exists(oldExeFile)) {
|
||||
// It's entirely possible that old process is still running, allow at least a second before trying to remove the file
|
||||
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
File.Delete(oldExeFile);
|
||||
@@ -172,7 +172,7 @@ namespace ArchiSteamFarm {
|
||||
if (!updateOverride && !GlobalConfig.AutoUpdates) {
|
||||
Logging.LogGenericInfo("New version is available!");
|
||||
Logging.LogGenericInfo("Consider updating yourself!");
|
||||
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -199,6 +199,9 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Downloading new version...");
|
||||
Logging.LogGenericInfo("While waiting, consider donating if you appreciate the work being done :)");
|
||||
|
||||
byte[] result = await WebBrowser.UrlGetToBytesRetry(binaryAsset.DownloadURL).ConfigureAwait(false);
|
||||
if (result == null) {
|
||||
return;
|
||||
@@ -247,17 +250,17 @@ namespace ArchiSteamFarm {
|
||||
|
||||
if (GlobalConfig.AutoRestart) {
|
||||
Logging.LogGenericInfo("Restarting...");
|
||||
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
Restart();
|
||||
} else {
|
||||
Logging.LogGenericInfo("Exiting...");
|
||||
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Exit(int exitCode = 0) {
|
||||
WCF.StopServer();
|
||||
Shutdown();
|
||||
Environment.Exit(exitCode);
|
||||
}
|
||||
|
||||
@@ -271,19 +274,19 @@ namespace ArchiSteamFarm {
|
||||
Exit();
|
||||
}
|
||||
|
||||
internal static string GetUserInput(EUserInputType userInputType, string botName = "Main", string extraInformation = null) {
|
||||
internal static string GetUserInput(EUserInputType userInputType, string botName = ASF, string extraInformation = null) {
|
||||
if (userInputType == EUserInputType.Unknown) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (GlobalConfig.Headless) {
|
||||
if (GlobalConfig.Headless || !Runtime.IsUserInteractive) {
|
||||
Logging.LogGenericWarning("Received a request for user input, but process is running in headless mode!");
|
||||
return null;
|
||||
}
|
||||
|
||||
string result;
|
||||
lock (ConsoleLock) {
|
||||
ConsoleIsBusy = true;
|
||||
Logging.OnUserInputStart();
|
||||
switch (userInputType) {
|
||||
case EUserInputType.DeviceID:
|
||||
Console.Write("<" + botName + "> Please enter your Device ID (including \"android:\"): ");
|
||||
@@ -327,13 +330,17 @@ namespace ArchiSteamFarm {
|
||||
Console.Clear(); // For security purposes
|
||||
}
|
||||
|
||||
ConsoleIsBusy = false;
|
||||
Logging.OnUserInputEnd();
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(result) ? result.Trim() : null;
|
||||
}
|
||||
|
||||
internal static void OnBotShutdown() {
|
||||
if (ShutdownResetEvent.IsSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Bot.Bots.Values.Any(bot => bot.KeepRunning)) {
|
||||
return;
|
||||
}
|
||||
@@ -347,6 +354,19 @@ namespace ArchiSteamFarm {
|
||||
ShutdownResetEvent.Set();
|
||||
}
|
||||
|
||||
private static void Shutdown() {
|
||||
if (ShutdownResetEvent.IsSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
ShutdownResetEvent.Set();
|
||||
WCF.StopServer();
|
||||
|
||||
foreach (Bot bot in Bot.Bots.Values) {
|
||||
bot.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitServices() {
|
||||
GlobalConfig = GlobalConfig.Load(Path.Combine(ConfigDirectory, GlobalConfigFile));
|
||||
if (GlobalConfig == null) {
|
||||
@@ -366,7 +386,7 @@ namespace ArchiSteamFarm {
|
||||
WebBrowser.Init();
|
||||
WCF.Init();
|
||||
|
||||
WebBrowser = new WebBrowser("Main");
|
||||
WebBrowser = new WebBrowser(ASF);
|
||||
}
|
||||
|
||||
private static void ParseArgs(IEnumerable<string> args) {
|
||||
@@ -388,13 +408,18 @@ namespace ArchiSteamFarm {
|
||||
break;
|
||||
default:
|
||||
if (arg.StartsWith("--", StringComparison.Ordinal)) {
|
||||
Logging.LogGenericWarning("Unrecognized parameter: " + arg);
|
||||
continue;
|
||||
if (arg.StartsWith("--cryptkey=", StringComparison.Ordinal) && (arg.Length > 11)) {
|
||||
CryptoHelper.SetEncryptionKey(arg.Substring(11));
|
||||
} else {
|
||||
Logging.LogGenericWarning("Unrecognized parameter: " + arg);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (Mode != EMode.Client) {
|
||||
Logging.LogGenericWarning("Ignoring command because --client wasn't specified: " + arg);
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Command sent: " + arg);
|
||||
@@ -417,7 +442,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericException((Exception) args.ExceptionObject);
|
||||
Logging.LogFatalException((Exception) args.ExceptionObject);
|
||||
}
|
||||
|
||||
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
|
||||
@@ -426,14 +451,16 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericException(args.Exception);
|
||||
Logging.LogFatalException(args.Exception);
|
||||
}
|
||||
|
||||
private static void Init(IEnumerable<string> args) {
|
||||
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
|
||||
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
|
||||
|
||||
Logging.InitCoreLoggers();
|
||||
Logging.LogGenericInfo("ASF V" + Version);
|
||||
|
||||
Directory.SetCurrentDirectory(ExecutableDirectory);
|
||||
InitServices();
|
||||
|
||||
@@ -477,7 +504,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// From now on it's server mode
|
||||
Logging.Init();
|
||||
Logging.InitEnhancedLoggers();
|
||||
|
||||
if (!Directory.Exists(ConfigDirectory)) {
|
||||
Logging.LogGenericError("Config directory doesn't exist!");
|
||||
@@ -517,13 +544,37 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private static void Main(string[] args) {
|
||||
Init(args);
|
||||
if (Runtime.IsUserInteractive) {
|
||||
// App
|
||||
Init(args);
|
||||
|
||||
// Wait for signal to shutdown
|
||||
ShutdownResetEvent.Wait();
|
||||
// Wait for signal to shutdown
|
||||
ShutdownResetEvent.Wait();
|
||||
|
||||
// We got a signal to shutdown
|
||||
Exit();
|
||||
// We got a signal to shutdown
|
||||
Exit();
|
||||
} else {
|
||||
// Service
|
||||
IsRunningAsService = true;
|
||||
using (Service service = new Service()) {
|
||||
ServiceBase.Run(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Service : ServiceBase {
|
||||
internal Service() {
|
||||
ServiceName = SharedInfo.ServiceName;
|
||||
}
|
||||
|
||||
protected override void OnStart(string[] args) => Task.Run(() => {
|
||||
Init(args);
|
||||
ShutdownResetEvent.Wait();
|
||||
Stop();
|
||||
});
|
||||
|
||||
protected override void OnStop() => Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using ArchiSteamFarm;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
@@ -9,7 +10,7 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ArchiSteamFarm")]
|
||||
[assembly: AssemblyCopyright("Copyright © ArchiSteamFarm 2015-2016")]
|
||||
[assembly: AssemblyCopyright(SharedInfo.Copyright)]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -31,5 +32,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.2")]
|
||||
[assembly: AssemblyFileVersion("2.0.5.2")]
|
||||
[assembly: AssemblyVersion(SharedInfo.Version)]
|
||||
[assembly: AssemblyFileVersion(SharedInfo.Version)]
|
||||
|
||||
108
ArchiSteamFarm/Runtime.cs
Normal file
108
ArchiSteamFarm/Runtime.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
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 Runtime {
|
||||
private static readonly Type MonoRuntime = Type.GetType("Mono.Runtime");
|
||||
private static bool IsRunningOnMono => MonoRuntime != null;
|
||||
|
||||
private static bool? _IsUserInteractive;
|
||||
internal static bool IsUserInteractive {
|
||||
get {
|
||||
if (_IsUserInteractive.HasValue) {
|
||||
return _IsUserInteractive.Value;
|
||||
}
|
||||
|
||||
if (Environment.UserInteractive) {
|
||||
_IsUserInteractive = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it's non-Mono, we can trust the result
|
||||
if (!IsRunningOnMono) {
|
||||
_IsUserInteractive = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// In Mono, Environment.UserInteractive is always false
|
||||
// There is really no reliable way for now, so assume always being interactive
|
||||
// Maybe in future I find out some awful hack or workaround that could be at least semi-reliable
|
||||
_IsUserInteractive = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool RequiresWorkaroundForMonoBug41701() {
|
||||
// Mono only, https://bugzilla.xamarin.com/show_bug.cgi?id=41701
|
||||
if (!IsRunningOnMono) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Version monoVersion = GetMonoVersion();
|
||||
if (monoVersion == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return monoVersion >= new Version(4, 4);
|
||||
}
|
||||
|
||||
private static Version GetMonoVersion() {
|
||||
if (MonoRuntime == null) {
|
||||
Logging.LogNullError(nameof(MonoRuntime));
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodInfo displayName = MonoRuntime.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
ArchiSteamFarm/SharedInfo.cs
Normal file
34
ArchiSteamFarm/SharedInfo.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class SharedInfo {
|
||||
internal const string Version = "2.1.2.0";
|
||||
internal const string Copyright = "Copyright © ArchiSteamFarm 2015-2016";
|
||||
|
||||
internal const string GithubRepo = "JustArchi/ArchiSteamFarm";
|
||||
internal const string ServiceName = "ArchiSteamFarm";
|
||||
internal const string ServiceDescription = "ASF is an application that allows you to farm steam cards using multiple steam accounts simultaneously.";
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
*/
|
||||
|
||||
using SteamAuth;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -32,12 +32,23 @@ using ArchiSteamFarm.JSON;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class Trading {
|
||||
private enum ParseTradeResult : byte {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
Unknown,
|
||||
Error,
|
||||
AcceptedWithItemLose,
|
||||
AcceptedWithoutItemLose,
|
||||
RejectedTemporarily,
|
||||
RejectedPermanently
|
||||
}
|
||||
|
||||
internal const byte MaxItemsPerTrade = 150; // This is due to limit on POST size in WebBrowser
|
||||
internal const byte MaxTradesPerAccount = 5; // This is limit introduced by Valve
|
||||
|
||||
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1);
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly ConcurrentHashSet<ulong> IgnoredTrades = new ConcurrentHashSet<ulong>();
|
||||
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1);
|
||||
|
||||
private byte ParsingTasks;
|
||||
@@ -45,7 +56,7 @@ namespace ArchiSteamFarm {
|
||||
internal static async Task LimitInventoryRequestsAsync() {
|
||||
await InventorySemaphore.WaitAsync().ConfigureAwait(false);
|
||||
Task.Run(async () => {
|
||||
await Utilities.SleepAsync(Program.GlobalConfig.InventoryLimiterDelay * 1000).ConfigureAwait(false);
|
||||
await Task.Delay(Program.GlobalConfig.InventoryLimiterDelay * 1000).ConfigureAwait(false);
|
||||
InventorySemaphore.Release();
|
||||
}).Forget();
|
||||
}
|
||||
@@ -58,6 +69,8 @@ namespace ArchiSteamFarm {
|
||||
Bot = bot;
|
||||
}
|
||||
|
||||
internal void OnDisconnected() => IgnoredTrades.ClearAndTrim();
|
||||
|
||||
internal async Task CheckTrades() {
|
||||
lock (TradesSemaphore) {
|
||||
if (ParsingTasks >= 2) {
|
||||
@@ -82,80 +95,115 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
|
||||
HashSet<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetActiveTradeOffers();
|
||||
if ((tradeOffers == null) || (tradeOffers.Count == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tradeOffers.RemoveWhere(tradeoffer => tradeoffer.State != Steam.TradeOffer.ETradeOfferState.Active);
|
||||
tradeOffers.TrimExcess();
|
||||
|
||||
if (tradeOffers.Count == 0) {
|
||||
return;
|
||||
if (tradeOffers.RemoveWhere(tradeoffer => IgnoredTrades.Contains(tradeoffer.TradeOfferID)) > 0) {
|
||||
tradeOffers.TrimExcess();
|
||||
if (tradeOffers.Count == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
|
||||
await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
||||
ParseTradeResult[] results = await Task.WhenAll(tradeOffers.Select(ParseTrade)).ConfigureAwait(false);
|
||||
if (results.Any(result => result == ParseTradeResult.AcceptedWithItemLose)) {
|
||||
await Task.Delay(1000).ConfigureAwait(false); // Sometimes we can be too fast for Steam servers to generate confirmations, wait a short moment
|
||||
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) {
|
||||
private async Task<ParseTradeResult> ParseTrade(Steam.TradeOffer tradeOffer) {
|
||||
if (tradeOffer == null) {
|
||||
Logging.LogNullError(nameof(tradeOffer), Bot.BotName);
|
||||
return;
|
||||
return ParseTradeResult.Error;
|
||||
}
|
||||
|
||||
if (tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active) {
|
||||
return;
|
||||
return ParseTradeResult.Error;
|
||||
}
|
||||
|
||||
if (await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false)) {
|
||||
Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
|
||||
} else if (Bot.BotConfig.IsBotAccount) {
|
||||
Logging.LogGenericInfo("Rejecting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||
Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID);
|
||||
} else {
|
||||
Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||
ParseTradeResult result = await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false);
|
||||
switch (result) {
|
||||
case ParseTradeResult.AcceptedWithItemLose:
|
||||
case ParseTradeResult.AcceptedWithoutItemLose:
|
||||
Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||
return await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false) ? result : ParseTradeResult.Error;
|
||||
case ParseTradeResult.RejectedPermanently:
|
||||
case ParseTradeResult.RejectedTemporarily:
|
||||
if (result == ParseTradeResult.RejectedPermanently) {
|
||||
if (Bot.BotConfig.IsBotAccount) {
|
||||
Logging.LogGenericInfo("Rejecting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||
return Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID) ? result : ParseTradeResult.Error;
|
||||
}
|
||||
|
||||
IgnoredTrades.Add(tradeOffer.TradeOfferID);
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||
return result;
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> ShouldAcceptTrade(Steam.TradeOffer tradeOffer) {
|
||||
private async Task<ParseTradeResult> ShouldAcceptTrade(Steam.TradeOffer tradeOffer) {
|
||||
if (tradeOffer == null) {
|
||||
Logging.LogNullError(nameof(tradeOffer), Bot.BotName);
|
||||
return false;
|
||||
return ParseTradeResult.Error;
|
||||
}
|
||||
|
||||
// Always accept trades when we're not losing anything
|
||||
if (tradeOffer.ItemsToGive.Count == 0) {
|
||||
// Unless it's steam fuckup and we're dealing with broken trade
|
||||
return tradeOffer.ItemsToReceive.Count > 0;
|
||||
return tradeOffer.ItemsToReceive.Count > 0 ? ParseTradeResult.AcceptedWithoutItemLose : ParseTradeResult.RejectedTemporarily;
|
||||
}
|
||||
|
||||
// Always accept trades from SteamMasterID
|
||||
if ((tradeOffer.OtherSteamID64 != 0) && (tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID)) {
|
||||
return true;
|
||||
return ParseTradeResult.AcceptedWithItemLose;
|
||||
}
|
||||
|
||||
// If we don't have SteamTradeMatcher enabled, this is the end for us
|
||||
if (!Bot.BotConfig.SteamTradeMatcher) {
|
||||
return false;
|
||||
return ParseTradeResult.RejectedPermanently;
|
||||
}
|
||||
|
||||
// Decline trade if we're giving more count-wise
|
||||
if (tradeOffer.ItemsToGive.Count > tradeOffer.ItemsToReceive.Count) {
|
||||
return false;
|
||||
return ParseTradeResult.RejectedPermanently;
|
||||
}
|
||||
|
||||
// Decline trade if we're losing anything but steam cards, or if it's non-dupes trade
|
||||
if (!tradeOffer.IsSteamCardsOnlyTradeForUs() || !tradeOffer.IsPotentiallyDupesTradeForUs()) {
|
||||
return false;
|
||||
return ParseTradeResult.RejectedPermanently;
|
||||
}
|
||||
|
||||
// At this point we're sure that STM trade is valid
|
||||
|
||||
// Fetch trade hold duration
|
||||
byte? holdDuration = await Bot.ArchiWebHandler.GetTradeHoldDuration(tradeOffer.TradeOfferID).ConfigureAwait(false);
|
||||
if (!holdDuration.HasValue) {
|
||||
// If we can't get trade hold duration, reject trade temporarily
|
||||
return ParseTradeResult.RejectedTemporarily;
|
||||
}
|
||||
|
||||
// If user has a trade hold, we add extra logic
|
||||
if (holdDuration.Value > 0) {
|
||||
// If trade hold duration exceeds our max, or user asks for cards with short lifespan, reject the trade
|
||||
if ((holdDuration.Value > Program.GlobalConfig.MaxTradeHoldDuration) || tradeOffer.ItemsToGive.Any(item => GlobalConfig.GlobalBlacklist.Contains(item.RealAppID))) {
|
||||
return ParseTradeResult.RejectedPermanently;
|
||||
}
|
||||
}
|
||||
|
||||
// Now check if it's worth for us to do the trade
|
||||
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false);
|
||||
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
|
||||
return ParseTradeResult.AcceptedWithItemLose; // OK, assume that this trade is valid, we can't check our EQ
|
||||
}
|
||||
|
||||
// Get appIDs we're interested in
|
||||
@@ -167,7 +215,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// If for some reason Valve is talking crap and we can't find mentioned items, assume OK
|
||||
if (inventory.Count == 0) {
|
||||
return true;
|
||||
return ParseTradeResult.AcceptedWithItemLose;
|
||||
}
|
||||
|
||||
// Now let's create a map which maps items to their amount in our EQ
|
||||
@@ -183,14 +231,16 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// Calculate our value of items to give
|
||||
List<uint> amountsToGive = new List<uint>(tradeOffer.ItemsToGive.Count);
|
||||
Dictionary<ulong, uint> amountMapToGive = new Dictionary<ulong, uint>(amountMap);
|
||||
foreach (ulong key in tradeOffer.ItemsToGive.Select(item => item.ClassID)) {
|
||||
uint amount;
|
||||
if (!amountMap.TryGetValue(key, out amount)) {
|
||||
if (!amountMapToGive.TryGetValue(key, out amount)) {
|
||||
amountsToGive.Add(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
amountsToGive.Add(amount);
|
||||
amountMapToGive[key] = amount - 1; // We're giving one, so we have one less
|
||||
}
|
||||
|
||||
// Sort it ascending
|
||||
@@ -198,24 +248,27 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// Calculate our value of items to receive
|
||||
List<uint> amountsToReceive = new List<uint>(tradeOffer.ItemsToReceive.Count);
|
||||
Dictionary<ulong, uint> amountMapToReceive = new Dictionary<ulong, uint>(amountMap);
|
||||
foreach (ulong key in tradeOffer.ItemsToReceive.Select(item => item.ClassID)) {
|
||||
uint amount;
|
||||
if (!amountMap.TryGetValue(key, out amount)) {
|
||||
if (!amountMapToReceive.TryGetValue(key, out amount)) {
|
||||
amountsToReceive.Add(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
amountsToReceive.Add(amount);
|
||||
amountMapToReceive[key] = amount + 1; // We're getting one, so we have one more
|
||||
}
|
||||
|
||||
// Sort it ascending
|
||||
amountsToReceive.Sort();
|
||||
|
||||
// Check actual difference
|
||||
// We sum only values at proper indexes of giving, because user might be overpaying
|
||||
int difference = amountsToGive.Select((t, i) => (int) (t - amountsToReceive[i])).Sum();
|
||||
|
||||
// Trade is worth for us if the difference is greater than 0
|
||||
return difference > 0;
|
||||
return difference > 0 ? ParseTradeResult.AcceptedWithItemLose : ParseTradeResult.RejectedTemporarily;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -34,15 +33,6 @@ namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
|
||||
internal static void Forget(this Task task) { }
|
||||
|
||||
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
|
||||
if (action != null) {
|
||||
return Task.WhenAll(sequence.Select(action));
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(action));
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
internal static string GetCookieValue(this CookieContainer cookieContainer, string url, string name) {
|
||||
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(name)) {
|
||||
Logging.LogNullError(nameof(url) + " || " + nameof(name));
|
||||
@@ -62,13 +52,6 @@ namespace ArchiSteamFarm {
|
||||
return cookies.Count == 0 ? null : (from Cookie cookie in cookies where cookie.Name.Equals(name) select cookie.Value).FirstOrDefault();
|
||||
}
|
||||
|
||||
internal static Task SleepAsync(int miliseconds) {
|
||||
if (miliseconds >= 0) {
|
||||
return Task.Delay(miliseconds);
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(miliseconds));
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
internal static uint GetUnixTime() => (uint) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +80,14 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// Most web services expect that UserAgent is set, so we declare it globally
|
||||
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent);
|
||||
|
||||
// We should always operate in English language, declare it globally
|
||||
HttpClient.DefaultRequestHeaders.AcceptLanguage.ParseAdd("en-US,en;q=0.8,en-GB;q=0.6");
|
||||
}
|
||||
|
||||
internal async Task<bool> UrlHeadRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -97,13 +100,13 @@ namespace ArchiSteamFarm {
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
internal async Task<Uri> UrlHeadToUriRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -116,13 +119,13 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<byte[]> UrlGetToBytesRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -135,13 +138,13 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<string> UrlGetToContentRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -154,13 +157,13 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<HtmlDocument> UrlGetToHtmlDocumentRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -173,13 +176,13 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<JObject> UrlGetToJObjectRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -192,13 +195,13 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<XmlDocument> UrlGetToXMLRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -211,13 +214,13 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<bool> UrlPostRetry(string request, Dictionary<string, string> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -230,13 +233,13 @@ namespace ArchiSteamFarm {
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<byte[]> UrlGetToBytes(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -251,7 +254,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private async Task<string> UrlGetToContent(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -266,7 +269,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private async Task<HtmlDocument> UrlGetToHtmlDocument(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -282,7 +285,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private async Task<JObject> UrlGetToJObject(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -308,13 +311,13 @@ namespace ArchiSteamFarm {
|
||||
return await UrlRequest(request, HttpMethod.Get, null, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<XmlDocument> UrlGetToXML(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -337,7 +340,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private async Task<bool> UrlHead(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -351,13 +354,13 @@ namespace ArchiSteamFarm {
|
||||
return await UrlRequest(request, HttpMethod.Head, null, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<Uri> UrlHeadToUri(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -368,7 +371,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private async Task<bool> UrlPost(string request, Dictionary<string, string> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -382,13 +385,13 @@ namespace ArchiSteamFarm {
|
||||
return await UrlRequest(request, HttpMethod.Post, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(request));
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request) || (httpMethod == null)) {
|
||||
Logging.LogNullError(nameof(request) + " || " + nameof(httpMethod));
|
||||
Logging.LogNullError(nameof(request) + " || " + nameof(httpMethod), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -413,7 +416,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
"SteamOwnerID": 0,
|
||||
"MaxFarmingTime": 10,
|
||||
"IdleFarmingPeriod": 3,
|
||||
"FarmingDelay": 5,
|
||||
"LoginLimiterDelay": 7,
|
||||
"FarmingDelay": 15,
|
||||
"LoginLimiterDelay": 10,
|
||||
"InventoryLimiterDelay": 3,
|
||||
"GiftsLimiterDelay": 1,
|
||||
"MaxTradeHoldDuration": 15,
|
||||
"ForceHttp": false,
|
||||
"HttpTimeout": 60,
|
||||
"WCFHostname": "localhost",
|
||||
@@ -23,6 +25,7 @@
|
||||
303700,
|
||||
335590,
|
||||
368020,
|
||||
425280
|
||||
425280,
|
||||
480730
|
||||
]
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
"StartOnLaunch": true,
|
||||
"SteamLogin": null,
|
||||
"SteamPassword": null,
|
||||
"PasswordFormat": 0,
|
||||
"SteamParentalPIN": "0",
|
||||
"SteamApiKey": null,
|
||||
"SteamMasterID": 0,
|
||||
@@ -16,7 +17,6 @@
|
||||
"SteamTradeMatcher": false,
|
||||
"ForwardKeysToOtherBots": false,
|
||||
"DistributeKeys": false,
|
||||
"UseAsfAsMobileAuthenticator": false,
|
||||
"ShutdownOnFarmingFinished": false,
|
||||
"SendOnFarmingFinished": false,
|
||||
"SteamTradeToken": null,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="HtmlAgilityPack" version="1.4.9" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1-beta1" targetFramework="net451" />
|
||||
<package id="Costura.Fody" version="2.0.0-beta0018" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="Fody" version="1.30.0-beta01" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="HtmlAgilityPack" version="1.4.9.4" targetFramework="net461" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
|
||||
<package id="NLog" version="4.4.0-beta13" targetFramework="net461" />
|
||||
<package id="protobuf-net" version="2.0.0.668" targetFramework="net45" />
|
||||
<package id="SteamKit2" version="1.7.0" targetFramework="net452" />
|
||||
</packages>
|
||||
30
CONTRIBUTING.md
Normal file
30
CONTRIBUTING.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Contributing
|
||||
|
||||
Before making an issue or pull request, you should carefully read **[ASF wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki)** first.
|
||||
|
||||
## Issues
|
||||
|
||||
GitHub **[issues](https://github.com/JustArchi/ArchiSteamFarm/issues)** page is being used for ASF TODO list, regarding both features and bugs. It has rather strict policy - GitHub is not technical support and all cases that are not suggestions or bug reports should NOT be posted there. You have **[ASF chat](https://gitter.im/JustArchi/ArchiSteamFarm)** and **[Steam group](http://steamcommunity.com/groups/ascfarm/discussions/1/)** for general discussion, questions or technical issues. Please avoid using GitHub issues, unless you indeed want to report a bug or suggest an enhancement. Even prior to doing that, please make sure that you're indeed dealing with a bug, or your suggestion makes sense, preferably by asking on chat/steam group first. Invalid issues will be closed immediately.
|
||||
|
||||
---
|
||||
|
||||
### Bugs
|
||||
|
||||
Posting a log is **mandatory**, regardless if it contains information that is relevant or not. You're allowed to make small modifications such as changing bot names to something more generic, but you should not be doing anything else. You want us to fix the bug you've encountered, then help us instead of making it harder - we're not being paid for that, and we're not forced to fix the bug you've encountered. Include as much relevant info as possible - if bug is reproducable, when it happens, if it's a result of a command - which one, does it happen always or only sometimes, with one account or all of them - everything you consider appropriate, that would help us reproduce the bug and fix it. The more information you include, the higher the chance of bug getting fixed. And this is probably what you want, right?
|
||||
|
||||
---
|
||||
|
||||
### Suggestions
|
||||
|
||||
ASF has rather strict scope - farming Steam cards from Steam games, which means that anything going greatly out of the scope will not be accepted, even if it's considered useful. A good example of that is Steam discovery queue, that provides extra cards during Steam sales - this is out of the scope of ASF as a program, ASF focuses on one task and is doing it efficiently, if you want to create your own bot that does exactly what you want - pay somebody for creating it.
|
||||
|
||||
If your suggestion doesn't go out of the scope of ASF, then explain to us in the issue why you consider it useful, why do you think that adding it to ASF is beneficial for **all users**, not yourself. Why we should spend our time coding it, convince us. If suggestion indeed makes sense, or can be considered practical, most likely we won't have anything against that, but **you** should be the one pointing out advantages, not us.
|
||||
|
||||
---
|
||||
|
||||
## Pull requests
|
||||
|
||||
In general any pull request is welcome and should be accepted, unless there is a strong reason against it. A strong reason includes e.g. a feature going potentially out of the scope of ASF. If you're improving existing codebase, rewriting code to be more efficient, clean, better commented - there is absolutely no reason to reject it. If you want to add missing feature, and you're not sure if it should be included in ASF, it won't hurt to ask before spending your own time.
|
||||
|
||||
Every pull request is carefully examined by our continuous integration system - it won't be accepted if it doesn't compile properly or causes any test to fail. We also expect that you at least barely tested the modification you're trying to add, and not blindly editing the file without even checking if it compiles. Consider the fact that you're not coding it only for yourself, but for thousands of users.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -30,8 +30,17 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
|
||||
[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal sealed class BotConfig : ASFConfig {
|
||||
internal enum ECryptoMethod : byte {
|
||||
PlainText,
|
||||
AES,
|
||||
ProtectedDataForCurrentUser
|
||||
}
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
@@ -41,9 +50,13 @@ namespace ConfigGenerator {
|
||||
[JsonProperty]
|
||||
public string SteamLogin { get; set; } = null;
|
||||
|
||||
[JsonProperty, PasswordPropertyText(true)]
|
||||
[JsonProperty]
|
||||
[PasswordPropertyText(true)]
|
||||
public string SteamPassword { get; set; } = null;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ECryptoMethod PasswordFormat { get; set; } = ECryptoMethod.PlainText;
|
||||
|
||||
[JsonProperty]
|
||||
public string SteamParentalPIN { get; set; } = "0";
|
||||
|
||||
@@ -83,9 +96,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;
|
||||
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
<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 />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -39,7 +42,7 @@
|
||||
</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>
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
@@ -48,6 +51,9 @@
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\ArchiSteamFarm\SharedInfo.cs">
|
||||
<Link>SharedInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="ASFConfig.cs" />
|
||||
<Compile Include="BotConfig.cs" />
|
||||
<Compile Include="DialogBox.cs" />
|
||||
@@ -101,18 +107,27 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="cirno.ico" />
|
||||
<None Include="FodyWeavers.xml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-ConfigGenerator.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
del "$(SolutionDir)out\ASF-ConfigGenerator.exe.config"
|
||||
copy "$(TargetDir)$(TargetName).exe" "$(SolutionDir)out\ASF-ConfigGenerator.exe"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mono --llvm --server -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-ConfigGenerator.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
rm "$(SolutionDir)out/ASF-ConfigGenerator.exe.config"
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Fody.1.30.0-beta01\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.30.0-beta01\build\dotnet\Fody.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Costura.Fody.2.0.0-beta0018\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.2.0.0-beta0018\build\Costura.Fody.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Fody.1.30.0-beta01\build\dotnet\Fody.targets" Condition="'$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' AND Exists('..\packages\Fody.1.30.0-beta01\build\dotnet\Fody.targets')" />
|
||||
<Import Project="..\packages\Costura.Fody.2.0.0-beta0018\build\Costura.Fody.targets" Condition="'$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' AND Exists('..\packages\Costura.Fody.2.0.0-beta0018\build\Costura.Fody.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">
|
||||
|
||||
4
ConfigGenerator/FodyWeavers.xml
Normal file
4
ConfigGenerator/FodyWeavers.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers>
|
||||
<Costura IncludeDebugSymbols='false' />
|
||||
</Weavers>
|
||||
@@ -30,9 +30,11 @@ using System.IO;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
|
||||
[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal sealed class GlobalConfig : ASFConfig {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal enum EUpdateChannel : byte {
|
||||
Unknown,
|
||||
Stable,
|
||||
@@ -40,13 +42,13 @@ namespace ConfigGenerator {
|
||||
}
|
||||
|
||||
private const byte DefaultMaxFarmingTime = 10;
|
||||
private const byte DefaultFarmingDelay = 5;
|
||||
private const byte DefaultFarmingDelay = 15;
|
||||
private const byte DefaultHttpTimeout = 60;
|
||||
private const ushort DefaultWCFPort = 1242;
|
||||
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
|
||||
|
||||
// This is hardcoded blacklist which should not be possible to change
|
||||
private static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280 };
|
||||
private static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280, 480730 };
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Debug { get; set; } = false;
|
||||
@@ -79,11 +81,17 @@ namespace ConfigGenerator {
|
||||
public byte FarmingDelay { get; set; } = DefaultFarmingDelay;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte LoginLimiterDelay { get; set; } = 7;
|
||||
public byte LoginLimiterDelay { get; set; } = 10;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte InventoryLimiterDelay { get; set; } = 3;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte GiftsLimiterDelay { get; set; } = 1;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte MaxTradeHoldDuration { get; set; } = 15;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool ForceHttp { get; set; } = false;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using ArchiSteamFarm;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal sealed partial class MainForm : Form {
|
||||
@@ -154,6 +155,19 @@ namespace ConfigGenerator {
|
||||
// Get rid of any potential whitespaces in bot name
|
||||
input = Regex.Replace(input, @"\s+", "");
|
||||
|
||||
if (string.IsNullOrEmpty(input)) {
|
||||
Logging.LogGenericErrorWithoutStacktrace("Your bot name is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (input) {
|
||||
case Program.ASF:
|
||||
case "example":
|
||||
case "minimal":
|
||||
Logging.LogGenericErrorWithoutStacktrace("This name is reserved!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ASFConfig.ASFConfigs.Select(config => Path.GetFileNameWithoutExtension(config.FilePath)).Any(fileNameWithoutExtension => (fileNameWithoutExtension == null) || fileNameWithoutExtension.Equals(input))) {
|
||||
Logging.LogGenericErrorWithoutStacktrace("Bot with such name exists already!");
|
||||
return;
|
||||
@@ -196,7 +210,7 @@ namespace ConfigGenerator {
|
||||
|
||||
args.Cancel = true;
|
||||
Tutorial.OnAction(Tutorial.EPhase.Help);
|
||||
Process.Start("https://github.com/JustArchi/ArchiSteamFarm/wiki/Configuration");
|
||||
Process.Start("https://github.com/" + SharedInfo.GithubRepo + "/wiki/Configuration");
|
||||
Tutorial.OnAction(Tutorial.EPhase.HelpFinished);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,12 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using ArchiSteamFarm;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal static class Program {
|
||||
@@ -35,8 +37,10 @@ namespace ConfigGenerator {
|
||||
internal const string GlobalConfigFile = ASF + ".json";
|
||||
|
||||
private const string ASFDirectory = "ArchiSteamFarm";
|
||||
private const string ASFExecutableFile = ASF + ".exe";
|
||||
|
||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
private static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
@@ -75,11 +79,30 @@ namespace ConfigGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
if (Directory.Exists(ConfigDirectory)) {
|
||||
if (!Directory.Exists(ConfigDirectory)) {
|
||||
Logging.LogGenericErrorWithoutStacktrace("Config directory could not be found!");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
if (!File.Exists(ASFExecutableFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericErrorWithoutStacktrace("Config directory could not be found!");
|
||||
FileVersionInfo asfVersionInfo = FileVersionInfo.GetVersionInfo(ASFExecutableFile);
|
||||
|
||||
Version asfVersion = new Version(asfVersionInfo.ProductVersion);
|
||||
if (Version == asfVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericErrorWithoutStacktrace(
|
||||
"Version of ASF and ConfigGenerator doesn't match!" + Environment.NewLine +
|
||||
"ASF version: " + asfVersion + " | ConfigGenerator version: " + Version + Environment.NewLine +
|
||||
Environment.NewLine +
|
||||
"Please use ConfigGenerator from the same ASF release, I'll redirect you to appropriate ASF release..."
|
||||
);
|
||||
|
||||
Process.Start("https://github.com/" + SharedInfo.GithubRepo + "/releases/tag/" + asfVersion);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using ArchiSteamFarm;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
@@ -9,7 +10,7 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ConfigGenerator")]
|
||||
[assembly: AssemblyCopyright("Copyright © ArchiSteamFarm 2015-2016")]
|
||||
[assembly: AssemblyCopyright(SharedInfo.Copyright)]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -31,5 +32,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("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyVersion(SharedInfo.Version)]
|
||||
[assembly: AssemblyFileVersion(SharedInfo.Version)]
|
||||
|
||||
28
ConfigGenerator/Properties/Settings.Designer.cs
generated
28
ConfigGenerator/Properties/Settings.Designer.cs
generated
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="9.0.1-beta1" targetFramework="net451" />
|
||||
<package id="Costura.Fody" version="2.0.0-beta0018" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="Fody" version="1.30.0-beta01" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
|
||||
</packages>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
13
README.md
13
README.md
@@ -1,11 +1,14 @@
|
||||
ArchiSteamFarm
|
||||
===================
|
||||
|
||||
[](https://ci.appveyor.com/project/JustArchi/ArchiSteamFarm)
|
||||
[](https://travis-ci.org/JustArchi/ArchiSteamFarm)
|
||||
[](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)
|
||||
[](https://github.com/JustArchi/ArchiSteamFarm/releases)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4)
|
||||
[](https://gitter.im/JustArchi/ArchiSteamFarm)
|
||||
[](https://ci.appveyor.com/project/JustArchi/ArchiSteamFarm)
|
||||
[](https://travis-ci.org/JustArchi/ArchiSteamFarm)
|
||||
[](./LICENSE-2.0.txt)
|
||||
[](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)
|
||||
[](https://github.com/JustArchi/ArchiSteamFarm/releases)
|
||||
|
||||
[](https://www.paypal.me/JustArchi/1usd)
|
||||
[](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)
|
||||
|
||||
---
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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")]
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="9.0.1-beta1" targetFramework="net451" />
|
||||
</packages>
|
||||
@@ -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
|
||||
|
||||
27
docs/CodeStyle.vssettings
Normal file
27
docs/CodeStyle.vssettings
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
BIN
packages/Costura.Fody.2.0.0-beta0018/Costura.Fody.dll
Normal file
BIN
packages/Costura.Fody.2.0.0-beta0018/Costura.Fody.dll
Normal file
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target
|
||||
AfterTargets="AfterBuild"
|
||||
Name="FodyPropertyChangedVerification">
|
||||
|
||||
<Error
|
||||
Condition="$(FodyExecutedWeavers) == '*Undefined*'"
|
||||
Text="Could not find `FodyWeaversExecuted` MSBuild property. It is possible you need to update Fody." />
|
||||
|
||||
<Error
|
||||
Condition="!$(FodyExecutedWeavers.Contains('Costura;'))"
|
||||
Text="Costura expected to be executed. You may also need to manually add '<Costura />' into your FodyWeavers.xml. eg <Weavers><Costura/></Weavers>. See https://github.com/Fody/Fody/wiki/SampleUsage" />
|
||||
|
||||
</Target>
|
||||
</Project>
|
||||
94
packages/Costura.Fody.2.0.0-beta0018/tools/install.ps1
Normal file
94
packages/Costura.Fody.2.0.0-beta0018/tools/install.ps1
Normal file
@@ -0,0 +1,94 @@
|
||||
param($installPath, $toolsPath, $package, $project)
|
||||
|
||||
|
||||
function RemoveForceProjectLevelHack($project)
|
||||
{
|
||||
Write-Host "RemoveForceProjectLevelHack"
|
||||
Foreach ($item in $project.ProjectItems)
|
||||
{
|
||||
if ($item.Name -eq "Fody_ToBeDeleted.txt")
|
||||
{
|
||||
$item.Delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function FlushVariables()
|
||||
{
|
||||
Write-Host "Flushing environment variables"
|
||||
$env:FodyLastProjectPath = ""
|
||||
$env:FodyLastWeaverName = ""
|
||||
$env:FodyLastXmlContents = ""
|
||||
}
|
||||
|
||||
function Update-FodyConfig($addinName, $project)
|
||||
{
|
||||
Write-Host "Update-FodyConfig"
|
||||
$fodyWeaversPath = [System.IO.Path]::Combine([System.IO.Path]::GetDirectoryName($project.FullName), "FodyWeavers.xml")
|
||||
|
||||
$FodyLastProjectPath = $env:FodyLastProjectPath
|
||||
$FodyLastWeaverName = $env:FodyLastWeaverName
|
||||
$FodyLastXmlContents = $env:FodyLastXmlContents
|
||||
|
||||
if (
|
||||
($FodyLastProjectPath -eq $project.FullName) -and
|
||||
($FodyLastWeaverName -eq $addinName))
|
||||
{
|
||||
Write-Host "Upgrade detected. Restoring content for $addinName"
|
||||
[System.IO.File]::WriteAllText($fodyWeaversPath, $FodyLastXmlContents)
|
||||
FlushVariables
|
||||
return
|
||||
}
|
||||
|
||||
FlushVariables
|
||||
|
||||
$xml = [xml](get-content $fodyWeaversPath)
|
||||
|
||||
$weavers = $xml["Weavers"]
|
||||
$node = $weavers.SelectSingleNode($addinName)
|
||||
|
||||
if (-not $node)
|
||||
{
|
||||
Write-Host "Appending node"
|
||||
$newNode = $xml.CreateElement($addinName)
|
||||
$weavers.AppendChild($newNode)
|
||||
}
|
||||
|
||||
$xml.Save($fodyWeaversPath)
|
||||
}
|
||||
|
||||
function Fix-ReferencesCopyLocal($package, $project)
|
||||
{
|
||||
Write-Host "Fix-ReferencesCopyLocal $($package.Id)"
|
||||
$asms = $package.AssemblyReferences | %{$_.Name}
|
||||
|
||||
foreach ($reference in $project.Object.References)
|
||||
{
|
||||
if ($asms -contains $reference.Name + ".dll")
|
||||
{
|
||||
if($reference.CopyLocal -eq $true)
|
||||
{
|
||||
$reference.CopyLocal = $false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function UnlockWeaversXml($project)
|
||||
{
|
||||
$fodyWeaversProjectItem = $project.ProjectItems.Item("FodyWeavers.xml");
|
||||
if ($fodyWeaversProjectItem)
|
||||
{
|
||||
$fodyWeaversProjectItem.Open("{7651A701-06E5-11D1-8EBD-00A0C90F26EA}")
|
||||
$fodyWeaversProjectItem.Save()
|
||||
$fodyWeaversProjectItem.Document.Close()
|
||||
}
|
||||
}
|
||||
|
||||
UnlockWeaversXml($project)
|
||||
|
||||
RemoveForceProjectLevelHack $project
|
||||
|
||||
Update-FodyConfig $package.Id.Replace(".Fody", "") $project
|
||||
|
||||
Fix-ReferencesCopyLocal $package $project
|
||||
47
packages/Costura.Fody.2.0.0-beta0018/tools/uninstall.ps1
Normal file
47
packages/Costura.Fody.2.0.0-beta0018/tools/uninstall.ps1
Normal file
@@ -0,0 +1,47 @@
|
||||
param($installPath, $toolsPath, $package, $project)
|
||||
|
||||
|
||||
function Update-FodyConfig($addinName, $project)
|
||||
{
|
||||
$fodyWeaversPath = [System.IO.Path]::Combine([System.IO.Path]::GetDirectoryName($project.FullName), "FodyWeavers.xml")
|
||||
|
||||
if (!(Test-Path ($fodyWeaversPath)))
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Caching variables for possible update"
|
||||
$env:FodyLastProjectPath = $project.FullName
|
||||
$env:FodyLastWeaverName = $addinName
|
||||
$env:FodyLastXmlContents = [IO.File]::ReadAllText($fodyWeaversPath)
|
||||
|
||||
|
||||
$xml = [xml](get-content $fodyWeaversPath)
|
||||
|
||||
$weavers = $xml["Weavers"]
|
||||
$node = $weavers.SelectSingleNode($addinName)
|
||||
|
||||
if ($node)
|
||||
{
|
||||
Write-Host "Removing node from FodyWeavers.xml"
|
||||
$weavers.RemoveChild($node)
|
||||
}
|
||||
|
||||
$xml.Save($fodyWeaversPath)
|
||||
}
|
||||
|
||||
|
||||
function UnlockWeaversXml($project)
|
||||
{
|
||||
$fodyWeaversProjectItem = $project.ProjectItems.Item("FodyWeavers.xml");
|
||||
if ($fodyWeaversProjectItem)
|
||||
{
|
||||
$fodyWeaversProjectItem.Open("{7651A701-06E5-11D1-8EBD-00A0C90F26EA}")
|
||||
$fodyWeaversProjectItem.Save()
|
||||
$fodyWeaversProjectItem.Document.Close()
|
||||
}
|
||||
}
|
||||
|
||||
UnlockWeaversXml($project)
|
||||
|
||||
Update-FodyConfig $package.Id.Replace(".Fody", "") $project
|
||||
4
packages/Fody.1.30.0-beta01/Content/FodyWeavers.xml
Normal file
4
packages/Fody.1.30.0-beta01/Content/FodyWeavers.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Weavers>
|
||||
|
||||
</Weavers>
|
||||
BIN
packages/Fody.1.30.0-beta01/Fody.1.30.0-beta01.nupkg
Normal file
BIN
packages/Fody.1.30.0-beta01/Fody.1.30.0-beta01.nupkg
Normal file
Binary file not shown.
BIN
packages/Fody.1.30.0-beta01/Fody.dll
Normal file
BIN
packages/Fody.1.30.0-beta01/Fody.dll
Normal file
Binary file not shown.
BIN
packages/Fody.1.30.0-beta01/FodyCommon.dll
Normal file
BIN
packages/Fody.1.30.0-beta01/FodyCommon.dll
Normal file
Binary file not shown.
BIN
packages/Fody.1.30.0-beta01/FodyIsolated.dll
Normal file
BIN
packages/Fody.1.30.0-beta01/FodyIsolated.dll
Normal file
Binary file not shown.
BIN
packages/Fody.1.30.0-beta01/Mono.Cecil.Mdb.dll
Normal file
BIN
packages/Fody.1.30.0-beta01/Mono.Cecil.Mdb.dll
Normal file
Binary file not shown.
BIN
packages/Fody.1.30.0-beta01/Mono.Cecil.Pdb.dll
Normal file
BIN
packages/Fody.1.30.0-beta01/Mono.Cecil.Pdb.dll
Normal file
Binary file not shown.
BIN
packages/Fody.1.30.0-beta01/Mono.Cecil.Rocks.dll
Normal file
BIN
packages/Fody.1.30.0-beta01/Mono.Cecil.Rocks.dll
Normal file
Binary file not shown.
BIN
packages/Fody.1.30.0-beta01/Mono.Cecil.dll
Normal file
BIN
packages/Fody.1.30.0-beta01/Mono.Cecil.dll
Normal file
Binary file not shown.
3
packages/Fody.1.30.0-beta01/Tools/install.ps1
Normal file
3
packages/Fody.1.30.0-beta01/Tools/install.ps1
Normal file
@@ -0,0 +1,3 @@
|
||||
param($installPath, $toolsPath, $package, $project)
|
||||
$item = $project.ProjectItems | where-object {$_.Name -eq "FodyWeavers.xml"}
|
||||
$item.Properties.Item("BuildAction").Value = [int]0
|
||||
98
packages/Fody.1.30.0-beta01/build/dotnet/Fody.targets
Normal file
98
packages/Fody.1.30.0-beta01/build/dotnet/Fody.targets
Normal file
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Choose>
|
||||
<When Condition="$(NCrunchOriginalSolutionDir) != '' And $(NCrunchOriginalSolutionDir) != '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodySolutionDir>$(NCrunchOriginalSolutionDir)</FodySolutionDir>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="$(SolutionDir) != '' And $(SolutionDir) != '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodySolutionDir>$(SolutionDir)</FodySolutionDir>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodySolutionDir>$(MSBuildProjectDirectory)..\..\..\</FodySolutionDir>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
<Choose>
|
||||
<When Condition="$(KeyOriginatorFile) != '' And $(KeyOriginatorFile) != '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodyKeyFilePath>$(KeyOriginatorFile)</FodyKeyFilePath>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="$(AssemblyOriginatorKeyFile) != '' And $(AssemblyOriginatorKeyFile) != '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodyKeyFilePath>$(AssemblyOriginatorKeyFile)</FodyKeyFilePath>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise >
|
||||
<PropertyGroup>
|
||||
<FodyKeyFilePath></FodyKeyFilePath>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<PropertyGroup>
|
||||
<ProjectWeaverXml>$(ProjectDir)FodyWeavers.xml</ProjectWeaverXml>
|
||||
<IntermediateDir>$(ProjectDir)$(IntermediateOutputPath)</IntermediateDir>
|
||||
<FodySignAssembly Condition="$(FodySignAssembly) == '' Or $(FodySignAssembly) == '*Undefined*'">$(SignAssembly)</FodySignAssembly>
|
||||
<FodyPath Condition="$(FodyPath) == '' Or $(FodyPath) == '*Undefined*'">$(MSBuildThisFileDirectory)..\..\</FodyPath>
|
||||
</PropertyGroup>
|
||||
<UsingTask
|
||||
TaskName="Fody.WeavingTask"
|
||||
AssemblyFile="$(FodyPath)\Fody.dll" />
|
||||
<Target
|
||||
AfterTargets="AfterCompile"
|
||||
Condition="Exists(@(IntermediateAssembly))"
|
||||
Name="FodyTarget"
|
||||
DependsOnTargets="$(FodyDependsOnTargets)"
|
||||
Inputs="@(IntermediateAssembly->'%(FullPath)');$(FodyKeyFilePath);$(ProjectWeaverXml)"
|
||||
Outputs="$(TargetPath)">
|
||||
|
||||
<Fody.WeavingTask
|
||||
AssemblyPath="@(IntermediateAssembly)"
|
||||
IntermediateDir="$(IntermediateDir)"
|
||||
KeyFilePath="$(FodyKeyFilePath)"
|
||||
NuGetPackageRoot="$(NuGetPackageRoot)"
|
||||
ProjectDirectory="$(ProjectDir)"
|
||||
SolutionDir="$(FodySolutionDir)"
|
||||
References="@(ReferencePath)"
|
||||
SignAssembly="$(FodySignAssembly)"
|
||||
ReferenceCopyLocalPaths="@(ReferenceCopyLocalPaths)"
|
||||
DefineConstants="$(DefineConstants)"
|
||||
>
|
||||
|
||||
<Output
|
||||
TaskParameter="ExecutedWeavers"
|
||||
PropertyName="FodyExecutedWeavers" />
|
||||
</Fody.WeavingTask>
|
||||
</Target>
|
||||
|
||||
|
||||
<UsingTask
|
||||
TaskName="Fody.VerifyTask"
|
||||
AssemblyFile="$(FodyPath)\Fody.dll" />
|
||||
<Target Condition="'$(NCrunch)' != '1' And Exists($(TargetPath))"
|
||||
AfterTargets="AfterBuild"
|
||||
Name="FodyVerifyTarget"
|
||||
DependsOnTargets="$(FodyVerifyDependsOnTargets)"
|
||||
Inputs="@(IntermediateAssembly->'%(FullPath)');$(FodyKeyFilePath);$(ProjectWeaverXml)"
|
||||
Outputs="$(TargetPath)">
|
||||
|
||||
<Fody.VerifyTask
|
||||
ProjectDirectory="$(ProjectDir)"
|
||||
TargetPath="$(TargetPath)"
|
||||
SolutionDir="$(FodySolutionDir)"
|
||||
DefineConstants="$(DefineConstants)"
|
||||
/>
|
||||
</Target>
|
||||
|
||||
|
||||
<!--Support for ncrunch-->
|
||||
<ItemGroup Condition="'$(NCrunch)' == '1'">
|
||||
<None Include="$(FodyPath)\*.*" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Choose>
|
||||
<When Condition="$(NCrunchOriginalSolutionDir) != '' And $(NCrunchOriginalSolutionDir) != '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodySolutionDir>$(NCrunchOriginalSolutionDir)</FodySolutionDir>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="$(SolutionDir) != '' And $(SolutionDir) != '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodySolutionDir>$(SolutionDir)</FodySolutionDir>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodySolutionDir>$(MSBuildProjectDirectory)..\..\..\</FodySolutionDir>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
<Choose>
|
||||
<When Condition="$(KeyOriginatorFile) != '' And $(KeyOriginatorFile) != '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodyKeyFilePath>$(KeyOriginatorFile)</FodyKeyFilePath>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="$(AssemblyOriginatorKeyFile) != '' And $(AssemblyOriginatorKeyFile) != '*Undefined*'">
|
||||
<PropertyGroup>
|
||||
<FodyKeyFilePath>$(AssemblyOriginatorKeyFile)</FodyKeyFilePath>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise >
|
||||
<PropertyGroup>
|
||||
<FodyKeyFilePath></FodyKeyFilePath>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<PropertyGroup>
|
||||
<ProjectWeaverXml>$(ProjectDir)FodyWeavers.xml</ProjectWeaverXml>
|
||||
<IntermediateDir>$(ProjectDir)$(IntermediateOutputPath)</IntermediateDir>
|
||||
<FodySignAssembly Condition="$(FodySignAssembly) == '' Or $(FodySignAssembly) == '*Undefined*'">$(SignAssembly)</FodySignAssembly>
|
||||
<FodyPath Condition="$(FodyPath) == '' Or $(FodyPath) == '*Undefined*'">$(MSBuildThisFileDirectory)..\..\</FodyPath>
|
||||
</PropertyGroup>
|
||||
<UsingTask
|
||||
TaskName="Fody.WeavingTask"
|
||||
AssemblyFile="$(FodyPath)\Fody.dll" />
|
||||
<Target
|
||||
AfterTargets="AfterCompile"
|
||||
Condition="Exists(@(IntermediateAssembly))"
|
||||
Name="FodyTarget"
|
||||
DependsOnTargets="$(FodyDependsOnTargets)"
|
||||
Inputs="@(IntermediateAssembly->'%(FullPath)');$(FodyKeyFilePath);$(ProjectWeaverXml)"
|
||||
Outputs="$(TargetPath)">
|
||||
|
||||
<Fody.WeavingTask
|
||||
AssemblyPath="@(IntermediateAssembly)"
|
||||
IntermediateDir="$(IntermediateDir)"
|
||||
KeyFilePath="$(FodyKeyFilePath)"
|
||||
NuGetPackageRoot="$(NuGetPackageRoot)"
|
||||
ProjectDirectory="$(ProjectDir)"
|
||||
SolutionDir="$(FodySolutionDir)"
|
||||
References="@(ReferencePath)"
|
||||
SignAssembly="$(FodySignAssembly)"
|
||||
ReferenceCopyLocalPaths="@(ReferenceCopyLocalPaths)"
|
||||
DefineConstants="$(DefineConstants)"
|
||||
>
|
||||
|
||||
<Output
|
||||
TaskParameter="ExecutedWeavers"
|
||||
PropertyName="FodyExecutedWeavers" />
|
||||
</Fody.WeavingTask>
|
||||
</Target>
|
||||
|
||||
|
||||
<UsingTask
|
||||
TaskName="Fody.VerifyTask"
|
||||
AssemblyFile="$(FodyPath)\Fody.dll" />
|
||||
<Target Condition="'$(NCrunch)' != '1' And Exists($(TargetPath))"
|
||||
AfterTargets="AfterBuild"
|
||||
Name="FodyVerifyTarget"
|
||||
DependsOnTargets="$(FodyVerifyDependsOnTargets)"
|
||||
Inputs="@(IntermediateAssembly->'%(FullPath)');$(FodyKeyFilePath);$(ProjectWeaverXml)"
|
||||
Outputs="$(TargetPath)">
|
||||
|
||||
<Fody.VerifyTask
|
||||
ProjectDirectory="$(ProjectDir)"
|
||||
TargetPath="$(TargetPath)"
|
||||
SolutionDir="$(FodySolutionDir)"
|
||||
DefineConstants="$(DefineConstants)"
|
||||
/>
|
||||
</Target>
|
||||
|
||||
|
||||
<!--Support for ncrunch-->
|
||||
<ItemGroup Condition="'$(NCrunch)' == '1'">
|
||||
<None Include="$(FodyPath)\*.*" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 699 B |
BIN
packages/HtmlAgilityPack.1.4.9.4/HtmlAgilityPack.1.4.9.4.nupkg
vendored
Normal file
BIN
packages/HtmlAgilityPack.1.4.9.4/HtmlAgilityPack.1.4.9.4.nupkg
vendored
Normal file
Binary file not shown.
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/Net20/HtmlAgilityPack.dll
vendored
Normal file
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/Net20/HtmlAgilityPack.dll
vendored
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/Net40-client/HtmlAgilityPack.dll
vendored
Normal file
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/Net40-client/HtmlAgilityPack.dll
vendored
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/Net40/HtmlAgilityPack.dll
vendored
Normal file
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/Net40/HtmlAgilityPack.dll
vendored
Normal file
Binary file not shown.
@@ -369,11 +369,12 @@
|
||||
<param name="node">The node to duplicate. May not be <c>null</c>.</param>
|
||||
<param name="deep">true to recursively clone the subtree under the specified node, false to clone only the node itself.</param>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.DescendantNodes">
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.DescendantNodes(System.Int32)">
|
||||
<summary>
|
||||
Gets all Descendant nodes for this node and each of child nodes
|
||||
</summary>
|
||||
<returns></returns>
|
||||
<param name="level">The depth level of the node to parse in the html tree</param>
|
||||
<returns>the current element as an HtmlNode</returns>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.DescendantNodesAndSelf">
|
||||
<summary>
|
||||
@@ -381,7 +382,7 @@
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.Descendants">
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.Descendants(System.Int32)">
|
||||
<summary>
|
||||
Gets all Descendant nodes in enumerated list
|
||||
</summary>
|
||||
@@ -520,11 +521,12 @@
|
||||
<param name="value">The value for the attribute.</param>
|
||||
<returns>The corresponding attribute instance.</returns>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteContentTo(System.IO.TextWriter)">
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteContentTo(System.IO.TextWriter,System.Int32)">
|
||||
<summary>
|
||||
Saves all the children of the node to the specified TextWriter.
|
||||
</summary>
|
||||
<param name="outText">The TextWriter to which you want to save.</param>
|
||||
<param name="level">Identifies the level we are in starting at root with 0</param>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteContentTo">
|
||||
<summary>
|
||||
@@ -532,11 +534,12 @@
|
||||
</summary>
|
||||
<returns>The saved string.</returns>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteTo(System.IO.TextWriter)">
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteTo(System.IO.TextWriter,System.Int32)">
|
||||
<summary>
|
||||
Saves the current node to the specified TextWriter.
|
||||
</summary>
|
||||
<param name="outText">The TextWriter to which you want to save.</param>
|
||||
<param name="level">identifies the level we are in starting at root with 0</param>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteTo(System.Xml.XmlWriter)">
|
||||
<summary>
|
||||
@@ -766,6 +769,11 @@
|
||||
</summary>
|
||||
<returns>An XPathNavigator object. The XPathNavigator is positioned on the root of the document.</returns>
|
||||
</member>
|
||||
<member name="F:HtmlAgilityPack.HtmlDocument._maxDepthLevel">
|
||||
<summary>
|
||||
Defines the max level we would go deep into the html document
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:HtmlAgilityPack.HtmlDocument.OptionAddDebuggingAttributes">
|
||||
<summary>
|
||||
Adds Debugging attributes to node. Default is false.
|
||||
@@ -1033,6 +1041,12 @@
|
||||
</summary>
|
||||
<param name="writer">The XmlWriter to which you want to save.</param>
|
||||
</member>
|
||||
<member name="P:HtmlAgilityPack.HtmlDocument.MaxDepthLevel">
|
||||
<summary>
|
||||
Defines the max level we would go deep into the html document. If this depth level is exceeded, and exception is
|
||||
thrown.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:HtmlAgilityPack.HtmlDocument.CheckSum">
|
||||
<summary>
|
||||
Gets the document CRC32 checksum if OptionComputeChecksum was set to true before parsing, 0 otherwise.
|
||||
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/Net45/HtmlAgilityPack.dll
vendored
Normal file
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/Net45/HtmlAgilityPack.dll
vendored
Normal file
Binary file not shown.
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/NetCore45/HtmlAgilityPack.dll
vendored
Normal file
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/NetCore45/HtmlAgilityPack.dll
vendored
Normal file
Binary file not shown.
@@ -465,11 +465,12 @@
|
||||
<param name="node">The node to duplicate. May not be <c>null</c>.</param>
|
||||
<param name="deep">true to recursively clone the subtree under the specified node, false to clone only the node itself.</param>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.DescendantNodes">
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.DescendantNodes(System.Int32)">
|
||||
<summary>
|
||||
Gets all Descendant nodes for this node and each of child nodes
|
||||
</summary>
|
||||
<returns></returns>
|
||||
<param name="level">The depth level of the node to parse in the html tree</param>
|
||||
<returns>the current element as an HtmlNode</returns>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.DescendantNodesAndSelf">
|
||||
<summary>
|
||||
@@ -477,7 +478,7 @@
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.Descendants">
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.Descendants(System.Int32)">
|
||||
<summary>
|
||||
Gets all Descendant nodes in enumerated list
|
||||
</summary>
|
||||
@@ -616,11 +617,12 @@
|
||||
<param name="value">The value for the attribute.</param>
|
||||
<returns>The corresponding attribute instance.</returns>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteContentTo(System.IO.TextWriter)">
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteContentTo(System.IO.TextWriter,System.Int32)">
|
||||
<summary>
|
||||
Saves all the children of the node to the specified TextWriter.
|
||||
</summary>
|
||||
<param name="outText">The TextWriter to which you want to save.</param>
|
||||
<param name="level">Identifies the level we are in starting at root with 0</param>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteContentTo">
|
||||
<summary>
|
||||
@@ -628,11 +630,12 @@
|
||||
</summary>
|
||||
<returns>The saved string.</returns>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteTo(System.IO.TextWriter)">
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteTo(System.IO.TextWriter,System.Int32)">
|
||||
<summary>
|
||||
Saves the current node to the specified TextWriter.
|
||||
</summary>
|
||||
<param name="outText">The TextWriter to which you want to save.</param>
|
||||
<param name="level">identifies the level we are in starting at root with 0</param>
|
||||
</member>
|
||||
<member name="M:HtmlAgilityPack.HtmlNode.WriteTo(System.Xml.XmlWriter)">
|
||||
<summary>
|
||||
@@ -786,6 +789,11 @@
|
||||
Represents a complete HTML document.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:HtmlAgilityPack.HtmlDocument._maxDepthLevel">
|
||||
<summary>
|
||||
Defines the max level we would go deep into the html document
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:HtmlAgilityPack.HtmlDocument.OptionAddDebuggingAttributes">
|
||||
<summary>
|
||||
Adds Debugging attributes to node. Default is false.
|
||||
@@ -1053,6 +1061,12 @@
|
||||
</summary>
|
||||
<param name="writer">The XmlWriter to which you want to save.</param>
|
||||
</member>
|
||||
<member name="P:HtmlAgilityPack.HtmlDocument.MaxDepthLevel">
|
||||
<summary>
|
||||
Defines the max level we would go deep into the html document. If this depth level is exceeded, and exception is
|
||||
thrown.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:HtmlAgilityPack.HtmlDocument.CheckSum">
|
||||
<summary>
|
||||
Gets the document CRC32 checksum if OptionComputeChecksum was set to true before parsing, 0 otherwise.
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/sl5/HtmlAgilityPack.dll
vendored
Normal file
BIN
packages/HtmlAgilityPack.1.4.9.4/lib/sl5/HtmlAgilityPack.dll
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user