mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-26 03:06:48 +00:00
Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54ad58a22d | ||
|
|
790e6baf46 | ||
|
|
c8fb715558 | ||
|
|
2ab5e6013d | ||
|
|
3e0c34e62c | ||
|
|
65b31e5537 | ||
|
|
62a6e38e47 | ||
|
|
0b50a45336 | ||
|
|
8d1d162b02 | ||
|
|
dbe13a1965 | ||
|
|
fc13633f5e | ||
|
|
d0cc10f3c6 | ||
|
|
567931a4cc | ||
|
|
396dc17ab2 | ||
|
|
288cc29338 | ||
|
|
844ca7da94 | ||
|
|
b14b9f87c7 | ||
|
|
8aa086cc27 | ||
|
|
cf00989d84 | ||
|
|
8f2f85282c | ||
|
|
fa12ffd9d0 | ||
|
|
ecb27adedd | ||
|
|
6e9be09944 | ||
|
|
6a79a89a10 | ||
|
|
d67be4f092 | ||
|
|
a8e1039e32 | ||
|
|
2ad9d9e197 | ||
|
|
fd6e2c72d7 | ||
|
|
1eed0f7647 | ||
|
|
c018c08260 | ||
|
|
72fa98cb89 | ||
|
|
71215d695e | ||
|
|
75b785d4b6 | ||
|
|
12e32692cb | ||
|
|
6158a9268d | ||
|
|
a48e53585a | ||
|
|
33383633ea | ||
|
|
3f7359c608 | ||
|
|
d8b59c6889 | ||
|
|
0f71b788cb | ||
|
|
d012255a21 | ||
|
|
8fc39a44cd | ||
|
|
c6fe424fcc | ||
|
|
12488dafd3 | ||
|
|
3b53491567 | ||
|
|
e337bd3856 | ||
|
|
cc47c0a764 | ||
|
|
22e4c0cd40 | ||
|
|
20b47e1787 | ||
|
|
941da0658d | ||
|
|
8048d4a7aa | ||
|
|
2554794daa | ||
|
|
4fcee90b99 | ||
|
|
761d73eb90 | ||
|
|
c163e5e2f3 | ||
|
|
8fd41cc587 | ||
|
|
f195563ba5 | ||
|
|
33df54365a | ||
|
|
4a6ae3064a | ||
|
|
84857e060b | ||
|
|
3311f2a703 | ||
|
|
5666ebf891 | ||
|
|
0ded4f3b80 | ||
|
|
4c9354308c | ||
|
|
0bcf2f35b7 | ||
|
|
b93d5187d6 | ||
|
|
1426922854 | ||
|
|
d8925f9409 | ||
|
|
4302029bee | ||
|
|
01a7800a44 | ||
|
|
ab3cf5b8d0 | ||
|
|
2ab9741fa8 | ||
|
|
f1c3339764 | ||
|
|
da01d4eab3 | ||
|
|
cd9bd5f0dd | ||
|
|
2602ac3623 | ||
|
|
2fe8db712e | ||
|
|
687b20bcb5 | ||
|
|
23d9dcffe7 | ||
|
|
42623ffb5a | ||
|
|
214003edbd | ||
|
|
c21333b6e9 | ||
|
|
630f1c008c |
10
.travis.yml
Normal file
10
.travis.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
sudo: false
|
||||
language: csharp
|
||||
solution: ArchiSteamFarm.sln
|
||||
|
||||
git:
|
||||
depth: 10
|
||||
|
||||
mono:
|
||||
- weekly
|
||||
- latest
|
||||
@@ -32,6 +32,16 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ArchiHandler : ClientMsgHandler {
|
||||
private readonly Bot Bot;
|
||||
|
||||
internal ArchiHandler(Bot bot) {
|
||||
if (bot == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bot = bot;
|
||||
}
|
||||
|
||||
/*
|
||||
____ _ _ _ _
|
||||
/ ___| __ _ | || || |__ __ _ ___ | | __ ___
|
||||
@@ -257,10 +267,11 @@ namespace ArchiSteamFarm {
|
||||
request.Body.key = key;
|
||||
|
||||
Client.Send(request);
|
||||
|
||||
try {
|
||||
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +90,7 @@
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<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>
|
||||
@@ -160,19 +157,19 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mkdir "$(SolutionDir)out" "$(SolutionDir)out\config"
|
||||
mkdir "$(SolutionDir)out\config"
|
||||
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.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
"$(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"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mkdir -p "$(SolutionDir)out" "$(SolutionDir)out/config"
|
||||
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 -O=all "$(SolutionDir)tools/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
mono -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"
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -32,19 +32,22 @@ using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Threading;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ArchiWebHandler {
|
||||
private const string SteamCommunity = "steamcommunity.com";
|
||||
private const byte MinSessionTTL = 15; // Assume session is valid for at least that amount of seconds
|
||||
|
||||
private static string SteamCommunityURL = "https://" + SteamCommunity;
|
||||
|
||||
private static int Timeout = 30 * 1000;
|
||||
private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000;
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly Dictionary<string, string> Cookie = new Dictionary<string, string>(4);
|
||||
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
|
||||
private readonly WebBrowser WebBrowser;
|
||||
|
||||
private ulong SteamID;
|
||||
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
|
||||
|
||||
internal static void Init() {
|
||||
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
||||
@@ -57,16 +60,18 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Bot = bot;
|
||||
|
||||
WebBrowser = new WebBrowser(bot.BotName);
|
||||
}
|
||||
|
||||
internal async Task<bool> Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
|
||||
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
|
||||
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SteamID = steamClient.SteamID;
|
||||
ulong steamID = steamClient.SteamID;
|
||||
|
||||
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString()));
|
||||
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
|
||||
|
||||
// Generate an AES session key
|
||||
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
|
||||
@@ -93,7 +98,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
try {
|
||||
authResult = iSteamUserAuth.AuthenticateUser(
|
||||
steamid: SteamID,
|
||||
steamid: steamID,
|
||||
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
|
||||
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
|
||||
method: WebRequestMethods.Http.Post,
|
||||
@@ -111,28 +116,26 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Logging.LogGenericInfo("Success!", Bot.BotName);
|
||||
|
||||
string steamLogin = authResult["token"].AsString();
|
||||
string steamLoginSecure = authResult["tokensecure"].AsString();
|
||||
WebBrowser.CookieContainer.Add(new Cookie("sessionid", sessionID, "/", "." + SteamCommunity));
|
||||
|
||||
Cookie["sessionid"] = sessionID;
|
||||
Cookie["steamLogin"] = steamLogin;
|
||||
Cookie["steamLoginSecure"] = steamLoginSecure;
|
||||
string steamLogin = authResult["token"].Value;
|
||||
WebBrowser.CookieContainer.Add(new Cookie("steamLogin", steamLogin, "/", "." + SteamCommunity));
|
||||
|
||||
// The below is used for display purposes only
|
||||
Cookie["webTradeEligibility"] = "{\"allowed\":0,\"reason\":0,\"allowed_at_time\":0,\"steamguard_required_days\":0,\"sales_this_year\":0,\"max_sales_per_year\":0,\"forms_requested\":0}";
|
||||
string steamLoginSecure = authResult["tokensecure"].Value;
|
||||
WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunity));
|
||||
|
||||
await UnlockParentalAccount(parentalPin).ConfigureAwait(false);
|
||||
if (!UnlockParentalAccount(parentalPin).Result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LastSessionRefreshCheck = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<bool?> IsLoggedIn() {
|
||||
if (SteamID == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HtmlDocument htmlDocument = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile", Cookie).ConfigureAwait(false);
|
||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (htmlDocument == null) {
|
||||
@@ -144,27 +147,46 @@ namespace ArchiSteamFarm {
|
||||
return htmlNode != null;
|
||||
}
|
||||
|
||||
internal async Task<bool> ReconnectIfNeeded() {
|
||||
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
||||
if (isLoggedIn.HasValue && !isLoggedIn.Value) {
|
||||
Logging.LogGenericInfo("Reconnecting because our sessionID expired!", Bot.BotName);
|
||||
Bot.RestartIfRunning().Forget();
|
||||
internal async Task<bool> RefreshSessionIfNeeded() {
|
||||
DateTime now = DateTime.Now;
|
||||
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
await SessionSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
now = DateTime.Now;
|
||||
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||
SessionSemaphore.Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
||||
if (isLoggedIn.GetValueOrDefault(true)) {
|
||||
result = true;
|
||||
now = DateTime.Now;
|
||||
LastSessionRefreshCheck = now;
|
||||
} else {
|
||||
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
|
||||
result = await Bot.RefreshSession().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SessionSemaphore.Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
|
||||
if (SteamID == 0) {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/profiles/" + SteamID + "/games/?xml=1";
|
||||
string request = SteamCommunityURL + "/my/games/?xml=1";
|
||||
|
||||
XmlDocument response = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
response = await WebBrowser.UrlGetToXML(request, Cookie).ConfigureAwait(false);
|
||||
response = await WebBrowser.UrlGetToXML(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
@@ -265,8 +287,13 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID;
|
||||
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Logging.LogNullError("sessionID");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -277,12 +304,12 @@ namespace ArchiSteamFarm {
|
||||
{"action", "join"}
|
||||
};
|
||||
|
||||
HttpResponseMessage response = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
response = await WebBrowser.UrlPost(request, data, Cookie).ConfigureAwait(false);
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
@@ -295,8 +322,13 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID;
|
||||
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Logging.LogNullError("sessionID");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -309,42 +341,12 @@ namespace ArchiSteamFarm {
|
||||
{"tradeofferid", tradeID.ToString()}
|
||||
};
|
||||
|
||||
HttpResponseMessage response = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
response = await WebBrowser.UrlPost(request, data, Cookie, referer).ConfigureAwait(false);
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool DeclineTradeOffer(ulong tradeID) {
|
||||
if (tradeID == 0 || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
||||
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++) {
|
||||
try {
|
||||
response = iEconService.DeclineTradeOffer(
|
||||
tradeofferid: tradeID.ToString(),
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: !Program.GlobalConfig.ForceHttp
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
@@ -353,9 +355,13 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
internal async Task<List<Steam.Item>> GetMyTradableInventory() {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JObject jObject = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
|
||||
jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/753/6?trading=1", Cookie).ConfigureAwait(false);
|
||||
jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/753/6?trading=1").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (jObject == null) {
|
||||
@@ -386,8 +392,13 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID;
|
||||
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Logging.LogNullError("sessionID");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -426,12 +437,12 @@ namespace ArchiSteamFarm {
|
||||
{"trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : $"{{\"trade_offer_access_token\":\"{token}\"}}"}
|
||||
};
|
||||
|
||||
HttpResponseMessage response = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
response = await WebBrowser.UrlPost(request, data, Cookie, referer).ConfigureAwait(false);
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
@@ -441,13 +452,17 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
internal async Task<HtmlDocument> GetBadgePage(byte page) {
|
||||
if (page == 0 || SteamID == 0) {
|
||||
if (page == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HtmlDocument htmlDocument = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/badges?l=english&p=" + page, Cookie).ConfigureAwait(false);
|
||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/badges?p=" + page).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (htmlDocument == null) {
|
||||
@@ -459,13 +474,17 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
|
||||
if (appID == 0 || SteamID == 0) {
|
||||
if (appID == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HtmlDocument htmlDocument = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/gamecards/" + appID + "?l=english", Cookie).ConfigureAwait(false);
|
||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/gamecards/" + appID).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (htmlDocument == null) {
|
||||
@@ -477,16 +496,16 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
internal async Task<bool> MarkInventory() {
|
||||
if (SteamID == 0) {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HttpResponseMessage response = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
response = await WebBrowser.UrlGet(SteamCommunityURL + "/profiles/" + SteamID + "/inventory", Cookie).ConfigureAwait(false);
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlGet(SteamCommunityURL + "/my/inventory").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
@@ -494,9 +513,42 @@ namespace ArchiSteamFarm {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task UnlockParentalAccount(string parentalPin) {
|
||||
internal async Task<bool> AcceptGift(ulong gid) {
|
||||
if (gid == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Logging.LogNullError("sessionID");
|
||||
return false;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/gifts/" + gid + "/acceptunpack";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
||||
{ "sessionid", sessionID }
|
||||
};
|
||||
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> UnlockParentalAccount(string parentalPin) {
|
||||
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
|
||||
@@ -509,20 +561,23 @@ namespace ArchiSteamFarm {
|
||||
|
||||
HttpResponseMessage response = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
response = await WebBrowser.UrlPost(request, data, Cookie, referer).ConfigureAwait(false);
|
||||
response = await WebBrowser.UrlPostToResponse(request, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
IEnumerable<string> setCookieValues;
|
||||
if (!response.Headers.TryGetValues("Set-Cookie", out setCookieValues)) {
|
||||
response.Dispose();
|
||||
Logging.LogNullError("setCookieValues", Bot.BotName);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
response.Dispose();
|
||||
|
||||
foreach (string setCookieValue in setCookieValues) {
|
||||
if (!setCookieValue.Contains("steamparental=")) {
|
||||
continue;
|
||||
@@ -535,12 +590,13 @@ namespace ArchiSteamFarm {
|
||||
setCookie = setCookie.Substring(0, index);
|
||||
}
|
||||
|
||||
Cookie["steamparental"] = setCookie;
|
||||
Logging.LogGenericInfo("Success!", Bot.BotName);
|
||||
return;
|
||||
WebBrowser.CookieContainer.Add(new Cookie("steamparental", setCookie, "/", "." + SteamCommunity));
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,6 @@ using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class BotConfig {
|
||||
@@ -66,6 +65,9 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool HandleOfflineMessages { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool AcceptGifts { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool ForwardKeysToOtherBots { get; private set; } = false;
|
||||
|
||||
@@ -97,14 +99,14 @@ namespace ArchiSteamFarm {
|
||||
internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint>() { 0 };
|
||||
|
||||
|
||||
internal static BotConfig Load(string path) {
|
||||
if (!File.Exists(path)) {
|
||||
internal static BotConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BotConfig botConfig;
|
||||
try {
|
||||
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(path));
|
||||
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
@@ -113,146 +115,7 @@ namespace ArchiSteamFarm {
|
||||
return botConfig;
|
||||
}
|
||||
|
||||
// TODO: This should be removed soon
|
||||
internal static BotConfig LoadOldFormat(string path) {
|
||||
if (!File.Exists(path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BotConfig botConfig = new BotConfig();
|
||||
|
||||
try {
|
||||
using (XmlReader reader = XmlReader.Create(path)) {
|
||||
while (reader.Read()) {
|
||||
if (reader.NodeType != XmlNodeType.Element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string key = reader.Name;
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string value = reader.GetAttribute("value");
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "Enabled":
|
||||
botConfig.Enabled = bool.Parse(value);
|
||||
break;
|
||||
case "SteamLogin":
|
||||
botConfig.SteamLogin = value;
|
||||
break;
|
||||
case "SteamPassword":
|
||||
botConfig.SteamPassword = value;
|
||||
break;
|
||||
case "SteamApiKey":
|
||||
botConfig.SteamApiKey = value;
|
||||
break;
|
||||
case "SteamTradeToken":
|
||||
botConfig.SteamTradeToken = value;
|
||||
break;
|
||||
case "SteamParentalPIN":
|
||||
botConfig.SteamParentalPIN = value;
|
||||
break;
|
||||
case "SteamMasterID":
|
||||
botConfig.SteamMasterID = ulong.Parse(value);
|
||||
break;
|
||||
case "SteamMasterClanID":
|
||||
botConfig.SteamMasterClanID = ulong.Parse(value);
|
||||
break;
|
||||
case "StartOnLaunch":
|
||||
botConfig.StartOnLaunch = bool.Parse(value);
|
||||
break;
|
||||
case "UseAsfAsMobileAuthenticator":
|
||||
botConfig.UseAsfAsMobileAuthenticator = bool.Parse(value);
|
||||
break;
|
||||
case "CardDropsRestricted":
|
||||
botConfig.CardDropsRestricted = bool.Parse(value);
|
||||
break;
|
||||
case "FarmOffline":
|
||||
botConfig.FarmOffline = bool.Parse(value);
|
||||
break;
|
||||
case "HandleOfflineMessages":
|
||||
botConfig.HandleOfflineMessages = bool.Parse(value);
|
||||
break;
|
||||
case "ForwardKeysToOtherBots":
|
||||
botConfig.ForwardKeysToOtherBots = bool.Parse(value);
|
||||
break;
|
||||
case "DistributeKeys":
|
||||
botConfig.DistributeKeys = bool.Parse(value);
|
||||
break;
|
||||
case "ShutdownOnFarmingFinished":
|
||||
botConfig.ShutdownOnFarmingFinished = bool.Parse(value);
|
||||
break;
|
||||
case "SendOnFarmingFinished":
|
||||
botConfig.SendOnFarmingFinished = bool.Parse(value);
|
||||
break;
|
||||
case "SendTradePeriod":
|
||||
botConfig.SendTradePeriod = byte.Parse(value);
|
||||
break;
|
||||
case "GamesPlayedWhileIdle":
|
||||
botConfig.GamesPlayedWhileIdle.Clear();
|
||||
foreach (string appID in value.Split(',')) {
|
||||
botConfig.GamesPlayedWhileIdle.Add(uint.Parse(appID));
|
||||
}
|
||||
break;
|
||||
case "Statistics":
|
||||
case "Blacklist":
|
||||
case "SteamNickname":
|
||||
break;
|
||||
default:
|
||||
Logging.LogGenericWarning("Unrecognized config value: " + key + "=" + value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
Logging.LogGenericError("Your config for this bot instance is invalid, it won't run!");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fixups for new format
|
||||
if (botConfig.SteamLogin != null && botConfig.SteamLogin.Equals("null")) {
|
||||
botConfig.SteamLogin = null;
|
||||
}
|
||||
|
||||
if (botConfig.SteamPassword != null && botConfig.SteamPassword.Equals("null")) {
|
||||
botConfig.SteamPassword = null;
|
||||
}
|
||||
|
||||
if (botConfig.SteamApiKey != null && botConfig.SteamApiKey.Equals("null")) {
|
||||
botConfig.SteamApiKey = null;
|
||||
}
|
||||
|
||||
if (botConfig.SteamParentalPIN != null && botConfig.SteamParentalPIN.Equals("null")) {
|
||||
botConfig.SteamParentalPIN = null;
|
||||
}
|
||||
|
||||
if (botConfig.SteamTradeToken != null && botConfig.SteamTradeToken.Equals("null")) {
|
||||
botConfig.SteamTradeToken = null;
|
||||
}
|
||||
|
||||
return botConfig;
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private BotConfig() { }
|
||||
|
||||
// TODO: This should be removed soon
|
||||
internal bool Convert(string path) {
|
||||
try {
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.LogGenericWarning("Your config was converted to new ASF V2.0 format");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,10 @@ namespace ArchiSteamFarm {
|
||||
private string FilePath;
|
||||
|
||||
internal static BotDatabase Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath)) {
|
||||
return new BotDatabase(filePath);
|
||||
}
|
||||
@@ -78,6 +82,10 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (botDatabase == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
botDatabase.FilePath = filePath;
|
||||
return botDatabase;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ namespace ArchiSteamFarm {
|
||||
private readonly Bot Bot;
|
||||
private readonly Timer Timer;
|
||||
|
||||
private volatile bool ManualMode, NowFarming;
|
||||
internal bool ManualMode { get; private set; }
|
||||
|
||||
private bool NowFarming;
|
||||
|
||||
internal CardsFarmer(Bot bot) {
|
||||
if (bot == null) {
|
||||
@@ -51,7 +53,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Bot = bot;
|
||||
|
||||
if (Program.GlobalConfig.IdleFarmingPeriod > 0) {
|
||||
if (Timer == null && Program.GlobalConfig.IdleFarmingPeriod > 0) {
|
||||
Timer = new Timer(
|
||||
async e => await CheckGamesForFarming().ConfigureAwait(false),
|
||||
null,
|
||||
@@ -61,9 +63,9 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<bool> SwitchToManualMode(bool manualMode) {
|
||||
internal async Task SwitchToManualMode(bool manualMode) {
|
||||
if (ManualMode == manualMode) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
ManualMode = manualMode;
|
||||
@@ -75,8 +77,6 @@ namespace ArchiSteamFarm {
|
||||
Logging.LogGenericInfo("Now running in Automatic Farming mode", Bot.BotName);
|
||||
StartFarming().Forget();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task StartFarming() {
|
||||
@@ -103,58 +103,51 @@ namespace ArchiSteamFarm {
|
||||
|
||||
bool farmedSomething = false;
|
||||
|
||||
// Now the algorithm used for farming depends on whether account is restricted or not
|
||||
if (Bot.BotConfig.CardDropsRestricted) { // If we have restricted card drops, we use complex algorithm
|
||||
Logging.LogGenericInfo("Chosen farming algorithm: Complex", Bot.BotName);
|
||||
while (GamesToFarm.Count > 0) {
|
||||
HashSet<uint> gamesToFarmSolo = GetGamesToFarmSolo(GamesToFarm);
|
||||
if (gamesToFarmSolo.Count > 0) {
|
||||
while (gamesToFarmSolo.Count > 0) {
|
||||
uint appID = gamesToFarmSolo.First();
|
||||
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
||||
farmedSomething = true;
|
||||
Logging.LogGenericInfo("Done farming: " + appID, Bot.BotName);
|
||||
gamesToFarmSolo.Remove(appID);
|
||||
gamesToFarmSolo.TrimExcess();
|
||||
do {
|
||||
// Now the algorithm used for farming depends on whether account is restricted or not
|
||||
if (Bot.BotConfig.CardDropsRestricted) { // If we have restricted card drops, we use complex algorithm
|
||||
Logging.LogGenericInfo("Chosen farming algorithm: Complex", Bot.BotName);
|
||||
while (GamesToFarm.Count > 0) {
|
||||
HashSet<uint> gamesToFarmSolo = GetGamesToFarmSolo(GamesToFarm);
|
||||
if (gamesToFarmSolo.Count > 0) {
|
||||
while (gamesToFarmSolo.Count > 0) {
|
||||
uint appID = gamesToFarmSolo.First();
|
||||
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
||||
farmedSomething = true;
|
||||
gamesToFarmSolo.Remove(appID);
|
||||
gamesToFarmSolo.TrimExcess();
|
||||
} else {
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FarmMultiple()) {
|
||||
Logging.LogGenericInfo("Done farming: " + string.Join(", ", GamesToFarm.Keys), Bot.BotName);
|
||||
} else {
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FarmMultiple(GamesToFarm)) {
|
||||
Logging.LogGenericInfo("Done farming: " + string.Join(", ", GamesToFarm.Keys), Bot.BotName);
|
||||
}
|
||||
} else { // If we have unrestricted card drops, we use simple algorithm
|
||||
Logging.LogGenericInfo("Chosen farming algorithm: Simple", Bot.BotName);
|
||||
while (GamesToFarm.Count > 0) {
|
||||
uint appID = GamesToFarm.Keys.FirstOrDefault();
|
||||
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
||||
farmedSomething = true;
|
||||
} else {
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // If we have unrestricted card drops, we use simple algorithm
|
||||
Logging.LogGenericInfo("Chosen farming algorithm: Simple", Bot.BotName);
|
||||
while (GamesToFarm.Count > 0) {
|
||||
uint appID = GetAnyGameToFarm(GamesToFarm);
|
||||
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
||||
farmedSomething = true;
|
||||
Logging.LogGenericInfo("Done farming: " + appID, Bot.BotName);
|
||||
} else {
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (await IsAnythingToFarm().ConfigureAwait(false));
|
||||
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
NowFarming = false;
|
||||
|
||||
// We finished our queue for now, make sure that everything is indeed farmed before proceeding further
|
||||
// Some games could be added in the meantime
|
||||
if (await IsAnythingToFarm().ConfigureAwait(false)) {
|
||||
StartFarming().Forget();
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
|
||||
await Bot.OnFarmingFinished(farmedSomething).ConfigureAwait(false);
|
||||
}
|
||||
@@ -208,23 +201,7 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static uint GetAnyGameToFarm(ConcurrentDictionary<uint, float> gamesToFarm) {
|
||||
if (gamesToFarm == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gamesToFarm.Keys.FirstOrDefault();
|
||||
}
|
||||
|
||||
private async Task<bool> IsAnythingToFarm() {
|
||||
if (NowFarming) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Checking badges...", Bot.BotName);
|
||||
|
||||
// Find the number of badge pages
|
||||
@@ -239,32 +216,29 @@ namespace ArchiSteamFarm {
|
||||
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
|
||||
if (htmlNodeCollection != null && htmlNodeCollection.Count > 0) {
|
||||
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
|
||||
if (!byte.TryParse(htmlNode.InnerText, out maxPages)) {
|
||||
maxPages = 1; // Should never happen
|
||||
string lastPage = htmlNode.InnerText;
|
||||
if (!string.IsNullOrEmpty(lastPage)) {
|
||||
if (!byte.TryParse(lastPage, out maxPages)) {
|
||||
maxPages = 1; // Should never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GamesToFarm.Clear();
|
||||
|
||||
// Find APPIDs we need to farm
|
||||
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
|
||||
CheckPage(htmlDocument);
|
||||
|
||||
List<Task> tasks = new List<Task>(maxPages - 1);
|
||||
for (byte page = 1; page <= maxPages; page++) {
|
||||
if (page == 1) {
|
||||
CheckPage(htmlDocument); // Because we fetched page number 1 already
|
||||
} else {
|
||||
byte currentPage = page; // We need a copy of variable being passed when in for loops
|
||||
if (maxPages > 1) {
|
||||
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
|
||||
List<Task> tasks = new List<Task>(maxPages - 1);
|
||||
for (byte page = 2; page <= maxPages; page++) {
|
||||
byte currentPage = page; // We need a copy of variable being passed when in for loops, as loop will proceed before task is launched
|
||||
tasks.Add(CheckPage(currentPage));
|
||||
}
|
||||
}
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
if (GamesToFarm.Count == 0) {
|
||||
return false;
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
return GamesToFarm.Count > 0;
|
||||
}
|
||||
|
||||
private void CheckPage(HtmlDocument htmlDocument) {
|
||||
@@ -289,7 +263,26 @@ namespace ArchiSteamFarm {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint appID = (uint) Utilities.OnlyNumbers(steamLink);
|
||||
int index = steamLink.LastIndexOf('/');
|
||||
if (index < 0) {
|
||||
Logging.LogNullError("index", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (steamLink.Length <= index) {
|
||||
Logging.LogNullError("length", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
steamLink = steamLink.Substring(index);
|
||||
|
||||
uint appID;
|
||||
if (!uint.TryParse(steamLink, out appID)) {
|
||||
Logging.LogNullError("appID", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (appID == 0) {
|
||||
Logging.LogNullError("appID", Bot.BotName);
|
||||
continue;
|
||||
@@ -302,22 +295,20 @@ namespace ArchiSteamFarm {
|
||||
HtmlNode timeNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']");
|
||||
if (timeNode == null) {
|
||||
Logging.LogNullError("timeNode", Bot.BotName);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
string hoursString = timeNode.InnerText;
|
||||
if (string.IsNullOrEmpty(hoursString)) {
|
||||
Logging.LogNullError("hoursString", Bot.BotName);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
hoursString = Regex.Match(hoursString, @"[0-9\.,]+").Value;
|
||||
float hours = 0;
|
||||
|
||||
float hours;
|
||||
if (string.IsNullOrEmpty(hoursString)) {
|
||||
hours = 0;
|
||||
} else {
|
||||
hours = float.Parse(hoursString, CultureInfo.InvariantCulture);
|
||||
Match match = Regex.Match(hoursString, @"[0-9\.,]+");
|
||||
if (match.Success) {
|
||||
float.TryParse(match.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out hours);
|
||||
}
|
||||
|
||||
GamesToFarm[appID] = hours;
|
||||
@@ -357,31 +348,33 @@ namespace ArchiSteamFarm {
|
||||
|
||||
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||
if (htmlNode == null) {
|
||||
await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
return !htmlNode.InnerText.Contains("No card drops");
|
||||
}
|
||||
|
||||
private bool FarmMultiple(ConcurrentDictionary<uint, float> appIDs) {
|
||||
if (appIDs.Count == 0) {
|
||||
private bool FarmMultiple() {
|
||||
if (GamesToFarm.Count == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
float maxHour = 0;
|
||||
foreach (float hour in appIDs.Values) {
|
||||
if (hour > maxHour) {
|
||||
maxHour = hour;
|
||||
foreach (KeyValuePair<uint, float> game in GamesToFarm) {
|
||||
CurrentGamesFarming.Add(game.Key);
|
||||
if (game.Value > maxHour) {
|
||||
maxHour = game.Value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (uint appID in appIDs.Keys) {
|
||||
CurrentGamesFarming.Add(appID);
|
||||
if (maxHour >= 2) {
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Now farming: " + string.Join(", ", appIDs.Keys), Bot.BotName);
|
||||
if (FarmHours(maxHour, appIDs.Keys)) {
|
||||
Logging.LogGenericInfo("Now farming: " + string.Join(", ", CurrentGamesFarming), Bot.BotName);
|
||||
if (FarmHours(maxHour, CurrentGamesFarming)) {
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
return true;
|
||||
@@ -403,8 +396,11 @@ namespace ArchiSteamFarm {
|
||||
if (await Farm(appID).ConfigureAwait(false)) {
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
float ignored;
|
||||
GamesToFarm.TryRemove(appID, out ignored);
|
||||
float hours;
|
||||
if (GamesToFarm.TryRemove(appID, out hours)) {
|
||||
TimeSpan timeSpan = TimeSpan.FromHours(hours);
|
||||
Logging.LogGenericInfo("Done farming: " + appID + " after " + timeSpan.ToString(@"hh\:mm") + " hours of playtime!", Bot.BotName);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
CurrentGamesFarming.Clear();
|
||||
@@ -428,6 +424,11 @@ namespace ArchiSteamFarm {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't forget to update our GamesToFarm hours
|
||||
float timePlayed = Program.GlobalConfig.FarmingDelay / 60.0F;
|
||||
GamesToFarm[appID] += timePlayed;
|
||||
|
||||
keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||
Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName);
|
||||
}
|
||||
@@ -437,7 +438,7 @@ namespace ArchiSteamFarm {
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool FarmHours(float maxHour, ICollection<uint> appIDs) {
|
||||
private bool FarmHours(float maxHour, HashSet<uint> appIDs) {
|
||||
if (maxHour < 0 || appIDs == null || appIDs.Count == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -450,7 +451,6 @@ namespace ArchiSteamFarm {
|
||||
|
||||
bool success = true;
|
||||
while (maxHour < 2) {
|
||||
Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName);
|
||||
if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||
success = false;
|
||||
break;
|
||||
@@ -458,15 +458,12 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// Don't forget to update our GamesToFarm hours
|
||||
float timePlayed = Program.GlobalConfig.FarmingDelay / 60.0F;
|
||||
foreach (KeyValuePair<uint, float> gameToFarm in GamesToFarm) {
|
||||
if (!appIDs.Contains(gameToFarm.Key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GamesToFarm[gameToFarm.Key] = gameToFarm.Value + timePlayed;
|
||||
foreach (uint appID in appIDs) {
|
||||
GamesToFarm[appID] += timePlayed;
|
||||
}
|
||||
|
||||
maxHour += timePlayed;
|
||||
Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName);
|
||||
}
|
||||
|
||||
Bot.ResetGamesPlayed();
|
||||
|
||||
@@ -36,12 +36,22 @@ namespace ArchiSteamFarm {
|
||||
Experimental
|
||||
}
|
||||
|
||||
internal const byte DefaultHttpTimeout = 60;
|
||||
|
||||
private const byte DefaultMaxFarmingTime = 10;
|
||||
private const byte DefaultFarmingDelay = 5;
|
||||
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 };
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool Debug { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool Headless { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool AutoUpdates { get; private set; } = true;
|
||||
|
||||
@@ -49,19 +59,19 @@ namespace ArchiSteamFarm {
|
||||
internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal ProtocolType SteamProtocol { get; private set; } = ProtocolType.Tcp;
|
||||
internal ProtocolType SteamProtocol { get; private set; } = DefaultSteamProtocol;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal ulong SteamOwnerID { get; private set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte MaxFarmingTime { get; private set; } = 10;
|
||||
internal byte MaxFarmingTime { get; private set; } = DefaultMaxFarmingTime;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte IdleFarmingPeriod { get; private set; } = 3;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte FarmingDelay { get; private set; } = 5;
|
||||
internal byte FarmingDelay { get; private set; } = DefaultFarmingDelay;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte AccountPlayingDelay { get; private set; } = 5;
|
||||
@@ -76,13 +86,13 @@ namespace ArchiSteamFarm {
|
||||
internal bool ForceHttp { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte HttpTimeout { get; private set; } = 60;
|
||||
internal byte HttpTimeout { get; private set; } = DefaultHttpTimeout;
|
||||
|
||||
[JsonProperty]
|
||||
internal string WCFHostname { get; set; } = "localhost";
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal string WCFHostname { get; private set; } = "localhost";
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal ushort WCFPort { get; private set; } = 1242;
|
||||
internal ushort WCFPort { get; private set; } = DefaultWCFPort;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool LogToFile { get; private set; } = true;
|
||||
@@ -111,18 +121,44 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (globalConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// SK2 supports only TCP and UDP steam protocols
|
||||
// Make sure that user can't screw this up
|
||||
// Ensure that user can't screw this up
|
||||
switch (globalConfig.SteamProtocol) {
|
||||
case ProtocolType.Tcp:
|
||||
case ProtocolType.Udp:
|
||||
break;
|
||||
default:
|
||||
Logging.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol + ", default TCP protocol will be used instead");
|
||||
globalConfig.SteamProtocol = ProtocolType.Tcp;
|
||||
Logging.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol + ". Value of " + DefaultSteamProtocol + " will be used instead");
|
||||
globalConfig.SteamProtocol = DefaultSteamProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
// User might not know what he's doing
|
||||
// Ensure that he can't screw core ASF variables
|
||||
if (globalConfig.MaxFarmingTime == 0) {
|
||||
Logging.LogGenericWarning("Configured MaxFarmingTime is invalid: " + globalConfig.MaxFarmingTime + ". Value of " + DefaultMaxFarmingTime + " will be used instead");
|
||||
globalConfig.MaxFarmingTime = DefaultMaxFarmingTime;
|
||||
}
|
||||
|
||||
if (globalConfig.FarmingDelay == 0) {
|
||||
Logging.LogGenericWarning("Configured FarmingDelay is invalid: " + globalConfig.FarmingDelay + ". Value of " + DefaultFarmingDelay + " will be used instead");
|
||||
globalConfig.FarmingDelay = DefaultFarmingDelay;
|
||||
}
|
||||
|
||||
if (globalConfig.HttpTimeout == 0) {
|
||||
Logging.LogGenericWarning("Configured HttpTimeout is invalid: " + globalConfig.HttpTimeout + ". Value of " + DefaultHttpTimeout + " will be used instead");
|
||||
globalConfig.HttpTimeout = DefaultHttpTimeout;
|
||||
}
|
||||
|
||||
if (globalConfig.WCFPort == 0) {
|
||||
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
|
||||
globalConfig.WCFPort = DefaultWCFPort;
|
||||
}
|
||||
|
||||
return globalConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
private uint _CellID = 0;
|
||||
private uint _CellID;
|
||||
|
||||
internal static GlobalDatabase Load() {
|
||||
if (!File.Exists(FilePath)) {
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace ArchiSteamFarm {
|
||||
internal static class Logging {
|
||||
private static readonly object FileLock = new object();
|
||||
|
||||
private static bool LogToFile = false;
|
||||
private static bool LogToFile;
|
||||
|
||||
internal static void Init() {
|
||||
LogToFile = Program.GlobalConfig.LogToFile;
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace ArchiSteamFarm {
|
||||
SteamParentalPIN,
|
||||
RevocationCode,
|
||||
TwoFactorAuthentication,
|
||||
WCFHostname
|
||||
}
|
||||
|
||||
internal enum EMode : byte {
|
||||
@@ -58,10 +59,11 @@ namespace ArchiSteamFarm {
|
||||
internal const string ConfigDirectory = "config";
|
||||
internal const string DebugDirectory = "debug";
|
||||
internal const string LogFile = "log.txt";
|
||||
internal const string GithubRepo = "JustArchi/ArchiSteamFarm";
|
||||
internal const string GlobalConfigFile = ASF + ".json";
|
||||
internal const string GlobalDatabaseFile = ASF + ".db";
|
||||
|
||||
private const string GithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases"; // GitHub API is HTTPS only
|
||||
private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only
|
||||
|
||||
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
|
||||
internal static readonly Version Version = Assembly.GetName().Version;
|
||||
@@ -80,17 +82,21 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static Timer AutoUpdatesTimer;
|
||||
private static EMode Mode = EMode.Normal;
|
||||
private static WebBrowser WebBrowser;
|
||||
|
||||
internal static async Task CheckForUpdate() {
|
||||
string oldExeFile = ExecutableFile + ".old";
|
||||
|
||||
// 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);
|
||||
|
||||
try {
|
||||
File.Delete(oldExeFile);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return;
|
||||
Logging.LogGenericError("Could not remove old ASF binary, please remove " + oldExeFile + " manually in order for update function to work!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +174,11 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (File.Exists(oldExeFile)) {
|
||||
Logging.LogGenericWarning("Refusing to proceed with auto update as old " + oldExeFile + " binary could not be removed, please remove it manually");
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto update logic starts here
|
||||
if (releaseResponse.Assets == null) {
|
||||
Logging.LogGenericWarning("Could not proceed with update because that version doesn't include assets!");
|
||||
@@ -245,13 +256,19 @@ namespace ArchiSteamFarm {
|
||||
Restart();
|
||||
}
|
||||
|
||||
internal static void Exit(int exitCode = 0) {
|
||||
WCF.StopServer();
|
||||
Environment.Exit(exitCode);
|
||||
}
|
||||
|
||||
internal static void Restart() {
|
||||
try {
|
||||
Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs().Skip(1)));
|
||||
Environment.Exit(0);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
|
||||
Exit();
|
||||
}
|
||||
|
||||
internal static async Task LimitSteamRequestsAsync() {
|
||||
@@ -262,46 +279,53 @@ namespace ArchiSteamFarm {
|
||||
}).Forget();
|
||||
}
|
||||
|
||||
internal static string GetUserInput(string botLogin, EUserInputType userInputType, string extraInformation = null) {
|
||||
internal static string GetUserInput(EUserInputType userInputType, string botName = null, string extraInformation = null) {
|
||||
if (userInputType == EUserInputType.Unknown) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (GlobalConfig.Headless) {
|
||||
Logging.LogGenericWarning("Received a request for user input, but process is running in headless mode!");
|
||||
return null;
|
||||
}
|
||||
|
||||
string result;
|
||||
lock (ConsoleLock) {
|
||||
ConsoleIsBusy = true;
|
||||
switch (userInputType) {
|
||||
case EUserInputType.DeviceID:
|
||||
Console.Write("<" + botLogin + "> Please enter your Device ID (including \"android:\"): ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your Device ID (including \"android:\"): ");
|
||||
break;
|
||||
case EUserInputType.Login:
|
||||
Console.Write("<" + botLogin + "> Please enter your login: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your login: ");
|
||||
break;
|
||||
case EUserInputType.Password:
|
||||
Console.Write("<" + botLogin + "> Please enter your password: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your password: ");
|
||||
break;
|
||||
case EUserInputType.PhoneNumber:
|
||||
Console.Write("<" + botLogin + "> Please enter your full phone number (e.g. +1234567890): ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your full phone number (e.g. +1234567890): ");
|
||||
break;
|
||||
case EUserInputType.SMS:
|
||||
Console.Write("<" + botLogin + "> Please enter SMS code sent on your mobile: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter SMS code sent on your mobile: ");
|
||||
break;
|
||||
case EUserInputType.SteamGuard:
|
||||
Console.Write("<" + botLogin + "> Please enter the auth code sent to your email: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter the auth code sent to your email: ");
|
||||
break;
|
||||
case EUserInputType.SteamParentalPIN:
|
||||
Console.Write("<" + botLogin + "> Please enter steam parental PIN: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter steam parental PIN: ");
|
||||
break;
|
||||
case EUserInputType.RevocationCode:
|
||||
Console.WriteLine("<" + botLogin + "> PLEASE WRITE DOWN YOUR REVOCATION CODE: " + extraInformation);
|
||||
Console.WriteLine("<" + botLogin + "> THIS IS THE ONLY WAY TO NOT GET LOCKED OUT OF YOUR ACCOUNT!");
|
||||
Console.Write("<" + botLogin + "> Hit enter once ready...");
|
||||
Console.WriteLine((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "PLEASE WRITE DOWN YOUR REVOCATION CODE: " + extraInformation);
|
||||
Console.Write("Hit enter once ready...");
|
||||
break;
|
||||
case EUserInputType.TwoFactorAuthentication:
|
||||
Console.Write("<" + botLogin + "> Please enter your 2 factor auth code from your authenticator app: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your 2 factor auth code from your authenticator app: ");
|
||||
break;
|
||||
case EUserInputType.WCFHostname:
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your WCF hostname: ");
|
||||
break;
|
||||
default:
|
||||
Console.Write("<" + botLogin + "> Please enter not documented yet value of \"" + userInputType + "\": ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter not documented yet value of \"" + userInputType + "\": ");
|
||||
break;
|
||||
}
|
||||
result = Console.ReadLine();
|
||||
@@ -333,19 +357,21 @@ namespace ArchiSteamFarm {
|
||||
if (GlobalConfig == null) {
|
||||
Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.json exists and is valid!");
|
||||
Thread.Sleep(5000);
|
||||
Environment.Exit(1);
|
||||
Exit(1);
|
||||
}
|
||||
|
||||
GlobalDatabase = GlobalDatabase.Load();
|
||||
if (GlobalDatabase == null) {
|
||||
Logging.LogGenericError("Global database could not be loaded!");
|
||||
Thread.Sleep(5000);
|
||||
Environment.Exit(1);
|
||||
Exit(1);
|
||||
}
|
||||
|
||||
ArchiWebHandler.Init();
|
||||
WebBrowser.Init();
|
||||
WCF.Init();
|
||||
|
||||
WebBrowser = new WebBrowser("Main");
|
||||
}
|
||||
|
||||
private static void ParseArgs(string[] args) {
|
||||
@@ -441,7 +467,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// If we ran ASF as a client, we're done by now
|
||||
if (Mode == EMode.Client) {
|
||||
Environment.Exit(0);
|
||||
Exit();
|
||||
}
|
||||
|
||||
// From now on it's server mode
|
||||
@@ -450,7 +476,7 @@ namespace ArchiSteamFarm {
|
||||
if (!Directory.Exists(ConfigDirectory)) {
|
||||
Logging.LogGenericError("Config directory doesn't exist!");
|
||||
Thread.Sleep(5000);
|
||||
Environment.Exit(1);
|
||||
Exit(1);
|
||||
}
|
||||
|
||||
CheckForUpdate().Wait();
|
||||
@@ -477,19 +503,6 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
// CONVERSION START
|
||||
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.xml")) {
|
||||
string botName = Path.GetFileNameWithoutExtension(configFile);
|
||||
Logging.LogGenericWarning("Found legacy " + botName + ".xml config file, it will now be converted to new ASF V2.0 format!");
|
||||
Bot bot = new Bot(botName);
|
||||
if (bot.BotConfig != null && bot.BotConfig.Enabled) {
|
||||
isRunning = true;
|
||||
} else {
|
||||
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
|
||||
}
|
||||
}
|
||||
// CONVERSION END
|
||||
|
||||
// Check if we got any bots running
|
||||
if (!isRunning) {
|
||||
OnBotShutdown();
|
||||
@@ -503,7 +516,7 @@ namespace ArchiSteamFarm {
|
||||
ShutdownResetEvent.WaitOne();
|
||||
|
||||
// We got a signal to shutdown
|
||||
Environment.Exit(0);
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,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.1.7")]
|
||||
[assembly: AssemblyFileVersion("2.0.1.7")]
|
||||
[assembly: AssemblyVersion("2.0.2.8")]
|
||||
[assembly: AssemblyFileVersion("2.0.2.8")]
|
||||
|
||||
@@ -35,9 +35,9 @@ namespace ArchiSteamFarm {
|
||||
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1);
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1);
|
||||
|
||||
private volatile byte ParsingTasks = 0;
|
||||
private byte ParsingTasks;
|
||||
|
||||
internal static async Task LimitInventoryRequestsAsync() {
|
||||
await InventorySemaphore.WaitAsync().ConfigureAwait(false);
|
||||
@@ -55,18 +55,27 @@ namespace ArchiSteamFarm {
|
||||
Bot = bot;
|
||||
}
|
||||
|
||||
internal async void CheckTrades() {
|
||||
if (ParsingTasks >= 2) {
|
||||
internal async Task CheckTrades() {
|
||||
bool shouldRun = false;
|
||||
lock (TradesSemaphore) {
|
||||
if (ParsingTasks < 2) {
|
||||
ParsingTasks++;
|
||||
shouldRun = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParsingTasks++;
|
||||
await TradesSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
await ParseActiveTrades().ConfigureAwait(false);
|
||||
Semaphore.Release();
|
||||
lock (TradesSemaphore) {
|
||||
ParsingTasks--;
|
||||
}
|
||||
|
||||
ParsingTasks--;
|
||||
TradesSemaphore.Release();
|
||||
}
|
||||
|
||||
private async Task ParseActiveTrades() {
|
||||
@@ -75,19 +84,12 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (Steam.TradeOffer tradeOffer in tradeOffers) {
|
||||
if (tradeOffer.trade_offer_state == Steam.TradeOffer.ETradeOfferState.Active) {
|
||||
tasks.Add(Task.Run(async () => await ParseTrade(tradeOffer).ConfigureAwait(false)));
|
||||
}
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
await Bot.AcceptConfirmations(Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
||||
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
|
||||
await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
|
||||
if (tradeOffer == null) {
|
||||
if (tradeOffer == null || tradeOffer.trade_offer_state != Steam.TradeOffer.ETradeOfferState.Active) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,12 +98,33 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tradeOffer.items_to_give.Count == 0 || tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID) {
|
||||
if (ShouldAcceptTrade(tradeOffer)) {
|
||||
Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
|
||||
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
|
||||
} else {
|
||||
Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldAcceptTrade(Steam.TradeOffer tradeOffer) {
|
||||
if (tradeOffer == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Always accept trades when we're not losing anything
|
||||
if (tradeOffer.items_to_give.Count == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Always accept trades from SteamMasterID
|
||||
if (tradeOffer.OtherSteamID64 != 0 && tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Add optional SteamTradeMatcher integration here
|
||||
|
||||
// If no rule above matched this trade, reject it
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,41 +22,47 @@
|
||||
|
||||
*/
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Utilities {
|
||||
internal static void Forget(this Task task) { }
|
||||
|
||||
internal static async Task SleepAsync(int miliseconds) {
|
||||
await Task.Delay(miliseconds).ConfigureAwait(false);
|
||||
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
|
||||
return Task.WhenAll(sequence.Select(action));
|
||||
}
|
||||
|
||||
internal static ulong OnlyNumbers(string inputString) {
|
||||
if (string.IsNullOrEmpty(inputString)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
string resultString = OnlyNumbersString(inputString);
|
||||
if (string.IsNullOrEmpty(resultString)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(resultString, out result)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static string OnlyNumbersString(string text) {
|
||||
if (string.IsNullOrEmpty(text)) {
|
||||
internal static string GetCookieValue(this CookieContainer cookieContainer, string URL, string name) {
|
||||
if (string.IsNullOrEmpty(URL) || string.IsNullOrEmpty(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Regex.Replace(text, @"[^\d]", "");
|
||||
CookieCollection cookies = cookieContainer.GetCookies(new Uri(URL));
|
||||
if (cookies == null || cookies.Count == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (Cookie cookie in cookies) {
|
||||
if (!cookie.Name.Equals(name, StringComparison.Ordinal)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return cookie.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static Task SleepAsync(int miliseconds) {
|
||||
if (miliseconds < 0) {
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
return Task.Delay(miliseconds);
|
||||
}
|
||||
|
||||
internal static uint GetCharCountInString(string s, char c) {
|
||||
|
||||
@@ -42,6 +42,13 @@ namespace ArchiSteamFarm {
|
||||
private Client Client;
|
||||
|
||||
internal static void Init() {
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
|
||||
Program.GlobalConfig.WCFHostname = Program.GetUserInput(Program.EUserInputType.WCFHostname);
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
|
||||
}
|
||||
|
||||
|
||||
@@ -23,37 +23,31 @@
|
||||
*/
|
||||
|
||||
using HtmlAgilityPack;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class WebBrowser {
|
||||
internal sealed class WebBrowser {
|
||||
internal const byte MaxRetries = 5; // Defines maximum number of retries, UrlRequest() does not handle retry by itself (it's app responsibility)
|
||||
|
||||
private const byte MaxConnections = 10; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
|
||||
private const byte MaxIdleTime = 15; // In seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
|
||||
|
||||
private static readonly string DefaultUserAgent = "ArchiSteamFarm/" + Program.Version;
|
||||
private static readonly HttpClient HttpClient = new HttpClient(new HttpClientHandler {
|
||||
UseCookies = false
|
||||
}) {
|
||||
Timeout = TimeSpan.FromSeconds(60)
|
||||
};
|
||||
|
||||
internal readonly CookieContainer CookieContainer = new CookieContainer();
|
||||
|
||||
private readonly HttpClient HttpClient;
|
||||
private readonly string Identifier;
|
||||
|
||||
internal static void Init() {
|
||||
HttpClient.Timeout = TimeSpan.FromSeconds(Program.GlobalConfig.HttpTimeout);
|
||||
|
||||
// Most web services expect that UserAgent is set, so we declare it globally
|
||||
// Any request can override that on as-needed basis (see: RequestOptions.FakeUserAgent)
|
||||
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent);
|
||||
|
||||
// Set max connection limit from default of 2 to desired value
|
||||
ServicePointManager.DefaultConnectionLimit = MaxConnections;
|
||||
|
||||
@@ -63,67 +57,114 @@ namespace ArchiSteamFarm {
|
||||
// Don't use Expect100Continue, we're sure about our POSTs, save some TCP packets
|
||||
ServicePointManager.Expect100Continue = false;
|
||||
|
||||
// Reuse ports if possible
|
||||
// TODO: Mono doesn't support that feature yet
|
||||
#if !__MonoCS__
|
||||
// Reuse ports if possible (since .NET 4.6+)
|
||||
//ServicePointManager.ReusePort = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static async Task<HttpResponseMessage> UrlGet(string request, Dictionary<string, string> cookies = null, string referer = null) {
|
||||
internal WebBrowser(string identifier) {
|
||||
if (string.IsNullOrEmpty(identifier)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Identifier = identifier;
|
||||
|
||||
HttpClientHandler httpClientHandler = new HttpClientHandler {
|
||||
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
|
||||
CookieContainer = CookieContainer
|
||||
};
|
||||
|
||||
HttpClient = new HttpClient(httpClientHandler) {
|
||||
Timeout = TimeSpan.FromSeconds(Program.GlobalConfig.HttpTimeout)
|
||||
};
|
||||
|
||||
// Most web services expect that UserAgent is set, so we declare it globally
|
||||
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent);
|
||||
}
|
||||
|
||||
internal async Task<bool> UrlGet(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HttpResponseMessage response = await UrlGetToResponse(request, referer).ConfigureAwait(false);
|
||||
if (response == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
response.Dispose();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<bool> UrlPost(string request, Dictionary<string, string> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HttpResponseMessage response = await UrlPostToResponse(request, data, referer).ConfigureAwait(false);
|
||||
if (response == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
response.Dispose();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<HttpResponseMessage> UrlGetToResponse(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await UrlRequest(request, HttpMethod.Get, null, cookies, referer).ConfigureAwait(false);
|
||||
return await UrlRequest(request, HttpMethod.Get, null, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal static async Task<HttpResponseMessage> UrlPost(string request, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null) {
|
||||
internal async Task<HttpResponseMessage> UrlPostToResponse(string request, Dictionary<string, string> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await UrlRequest(request, HttpMethod.Post, data, cookies, referer).ConfigureAwait(false);
|
||||
return await UrlRequest(request, HttpMethod.Post, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal static async Task<string> UrlGetToContent(string request, Dictionary<string, string> cookies = null, string referer = null) {
|
||||
internal async Task<string> UrlGetToContent(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpResponseMessage httpResponse = await UrlGet(request, cookies, referer).ConfigureAwait(false);
|
||||
HttpResponseMessage httpResponse = await UrlGetToResponse(request, referer).ConfigureAwait(false);
|
||||
if (httpResponse == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (httpResponse.Content == null) {
|
||||
return null;
|
||||
}
|
||||
string result = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
httpResponse.Dispose();
|
||||
|
||||
return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static async Task<Stream> UrlGetToStream(string request, Dictionary<string, string> cookies = null, string referer = null) {
|
||||
internal async Task<Stream> UrlGetToStream(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpResponseMessage httpResponse = await UrlGet(request, cookies, referer).ConfigureAwait(false);
|
||||
HttpResponseMessage httpResponse = await UrlGetToResponse(request, referer).ConfigureAwait(false);
|
||||
if (httpResponse == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (httpResponse.Content == null) {
|
||||
return null;
|
||||
}
|
||||
Stream result = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
httpResponse.Dispose();
|
||||
|
||||
return await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static async Task<HtmlDocument> UrlGetToHtmlDocument(string request, Dictionary<string, string> cookies = null, string referer = null) {
|
||||
internal async Task<HtmlDocument> UrlGetToHtmlDocument(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string content = await UrlGetToContent(request, cookies, referer).ConfigureAwait(false);
|
||||
string content = await UrlGetToContent(request, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
@@ -135,12 +176,12 @@ namespace ArchiSteamFarm {
|
||||
return htmlDocument;
|
||||
}
|
||||
|
||||
internal static async Task<JObject> UrlGetToJObject(string request, Dictionary<string, string> cookies = null, string referer = null) {
|
||||
internal async Task<JObject> UrlGetToJObject(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string content = await UrlGetToContent(request, cookies, referer).ConfigureAwait(false);
|
||||
string content = await UrlGetToContent(request, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
@@ -149,20 +190,20 @@ namespace ArchiSteamFarm {
|
||||
|
||||
try {
|
||||
jObject = JObject.Parse(content);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
return jObject;
|
||||
}
|
||||
|
||||
internal static async Task<XmlDocument> UrlGetToXML(string request, Dictionary<string, string> cookies = null, string referer = null) {
|
||||
internal async Task<XmlDocument> UrlGetToXML(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string content = await UrlGetToContent(request, cookies, referer).ConfigureAwait(false);
|
||||
string content = await UrlGetToContent(request, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
@@ -172,37 +213,33 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
xmlDocument.LoadXml(content);
|
||||
} catch (XmlException e) {
|
||||
Logging.LogGenericException(e);
|
||||
Logging.LogGenericException(e, Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
return xmlDocument;
|
||||
}
|
||||
|
||||
private static async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null) {
|
||||
private async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request) || httpMethod == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (request.StartsWith("https://") && Program.GlobalConfig.ForceHttp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpResponseMessage responseMessage;
|
||||
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
|
||||
if (data != null && data.Count > 0) {
|
||||
try {
|
||||
requestMessage.Content = new FormUrlEncodedContent(data);
|
||||
} catch (UriFormatException e) {
|
||||
Logging.LogGenericException(e);
|
||||
Logging.LogGenericException(e, Identifier);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (cookies != null && cookies.Count > 0) {
|
||||
StringBuilder cookieHeader = new StringBuilder();
|
||||
foreach (KeyValuePair<string, string> cookie in cookies) {
|
||||
cookieHeader.Append(cookie.Key + "=" + cookie.Value + ";");
|
||||
}
|
||||
requestMessage.Headers.Add("Cookie", cookieHeader.ToString());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(referer)) {
|
||||
requestMessage.Headers.Referrer = new Uri(referer);
|
||||
}
|
||||
@@ -215,6 +252,14 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (responseMessage == null || !responseMessage.IsSuccessStatusCode) {
|
||||
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
||||
Logging.LogGenericError("Request: " + request + " failed!", Identifier);
|
||||
if (responseMessage != null) {
|
||||
Logging.LogGenericError("Status code: " + responseMessage.StatusCode, Identifier);
|
||||
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false), Identifier);
|
||||
responseMessage.Dispose();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"Debug": false,
|
||||
"Headless": false,
|
||||
"AutoUpdates": true,
|
||||
"UpdateChannel": 1,
|
||||
"SteamProtocol": 6,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"DismissInventoryNotifications": true,
|
||||
"FarmOffline": false,
|
||||
"HandleOfflineMessages": false,
|
||||
"AcceptGifts": false,
|
||||
"ForwardKeysToOtherBots": false,
|
||||
"DistributeKeys": false,
|
||||
"UseAsfAsMobileAuthenticator": false,
|
||||
|
||||
@@ -29,7 +29,7 @@ using System.IO;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal class ASFConfig {
|
||||
internal static List<ASFConfig> ASFConfigs = new List<ASFConfig>();
|
||||
internal static readonly HashSet<ASFConfig> ASFConfigs = new HashSet<ASFConfig>();
|
||||
|
||||
internal string FilePath { get; set; }
|
||||
|
||||
@@ -37,9 +37,8 @@ namespace ConfigGenerator {
|
||||
ASFConfigs.Add(this);
|
||||
}
|
||||
|
||||
protected ASFConfig(string filePath) {
|
||||
protected ASFConfig(string filePath) : this() {
|
||||
FilePath = filePath;
|
||||
ASFConfigs.Add(this);
|
||||
}
|
||||
|
||||
internal virtual void Save() {
|
||||
@@ -55,9 +54,9 @@ namespace ConfigGenerator {
|
||||
internal virtual void Remove() {
|
||||
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
||||
lock (FilePath) {
|
||||
foreach (var configFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
||||
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
||||
try {
|
||||
File.Delete(configFile);
|
||||
File.Delete(botFile);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
@@ -73,9 +72,9 @@ namespace ConfigGenerator {
|
||||
|
||||
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
||||
lock (FilePath) {
|
||||
foreach (var file in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
||||
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
||||
try {
|
||||
File.Move(file, Path.Combine(Program.ConfigDirectory, botName + Path.GetExtension(file)));
|
||||
File.Move(botFile, Path.Combine(Program.ConfigDirectory, botName + Path.GetExtension(botFile)));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
|
||||
@@ -65,6 +65,9 @@ namespace ConfigGenerator {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool HandleOfflineMessages { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool AcceptGifts { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool ForwardKeysToOtherBots { get; set; } = false;
|
||||
|
||||
@@ -95,7 +98,6 @@ namespace ConfigGenerator {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public List<uint> GamesPlayedWhileIdle { get; set; } = new List<uint>();
|
||||
|
||||
|
||||
internal static BotConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
return null;
|
||||
@@ -113,13 +115,17 @@ namespace ConfigGenerator {
|
||||
return new BotConfig(filePath);
|
||||
}
|
||||
|
||||
if (botConfig == null) {
|
||||
return new BotConfig(filePath);
|
||||
}
|
||||
|
||||
botConfig.FilePath = filePath;
|
||||
|
||||
return botConfig;
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private BotConfig() : base() { }
|
||||
private BotConfig() { }
|
||||
|
||||
private BotConfig(string filePath) : base(filePath) {
|
||||
FilePath = filePath;
|
||||
|
||||
@@ -44,15 +44,8 @@
|
||||
</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.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ASFConfig.cs" />
|
||||
@@ -111,13 +104,11 @@
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mkdir "$(SolutionDir)out" "$(SolutionDir)out\config"
|
||||
"$(SolutionDir)tools\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
del "$(SolutionDir)out\ASF-GUI.exe.config"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mkdir -p "$(SolutionDir)out" "$(SolutionDir)out/config"
|
||||
mono -O=all "$(SolutionDir)tools/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
rm "$(SolutionDir)out/ASF-GUI.exe.config"
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -27,12 +27,9 @@ using System.Windows.Forms;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal class ConfigPage : TabPage {
|
||||
|
||||
internal readonly ASFConfig ASFConfig;
|
||||
|
||||
private EnhancedPropertyGrid EnhancedPropertyGrid;
|
||||
|
||||
internal ConfigPage(ASFConfig config) : base() {
|
||||
internal ConfigPage(ASFConfig config) {
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
@@ -41,17 +38,12 @@ namespace ConfigGenerator {
|
||||
|
||||
RefreshText();
|
||||
|
||||
EnhancedPropertyGrid = new EnhancedPropertyGrid(config);
|
||||
Controls.Add(EnhancedPropertyGrid);
|
||||
EnhancedPropertyGrid enhancedPropertyGrid = new EnhancedPropertyGrid(config);
|
||||
Controls.Add(enhancedPropertyGrid);
|
||||
}
|
||||
|
||||
internal void RefreshText() {
|
||||
Text = Path.GetFileNameWithoutExtension(ASFConfig.FilePath);
|
||||
}
|
||||
|
||||
private void InitializeComponent() {
|
||||
SuspendLayout();
|
||||
ResumeLayout(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal static class Debugging {
|
||||
#if DEBUG
|
||||
|
||||
@@ -26,8 +26,9 @@ using System.Windows.Forms;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal sealed class EnhancedPropertyGrid : PropertyGrid {
|
||||
private ASFConfig ASFConfig;
|
||||
internal EnhancedPropertyGrid(ASFConfig config) : base() {
|
||||
private readonly ASFConfig ASFConfig;
|
||||
|
||||
internal EnhancedPropertyGrid(ASFConfig config) {
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
@@ -65,7 +66,6 @@ namespace ConfigGenerator {
|
||||
if (globalConfig.SteamOwnerID != 0) {
|
||||
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigReady);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,12 +36,21 @@ namespace ConfigGenerator {
|
||||
Experimental
|
||||
}
|
||||
|
||||
private const byte DefaultMaxFarmingTime = 10;
|
||||
private const byte DefaultFarmingDelay = 5;
|
||||
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
|
||||
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280 };
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Debug { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Headless { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool AutoUpdates { get; set; } = true;
|
||||
|
||||
@@ -49,19 +58,19 @@ namespace ConfigGenerator {
|
||||
public EUpdateChannel UpdateChannel { get; set; } = EUpdateChannel.Stable;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ProtocolType SteamProtocol { get; set; } = ProtocolType.Tcp;
|
||||
public ProtocolType SteamProtocol { get; set; } = DefaultSteamProtocol;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ulong SteamOwnerID { get; set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte MaxFarmingTime { get; set; } = 10;
|
||||
public byte MaxFarmingTime { get; set; } = DefaultMaxFarmingTime;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte IdleFarmingPeriod { get; set; } = 3;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte FarmingDelay { get; set; } = 5;
|
||||
public byte FarmingDelay { get; set; } = DefaultFarmingDelay;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte AccountPlayingDelay { get; set; } = 5;
|
||||
@@ -76,13 +85,13 @@ namespace ConfigGenerator {
|
||||
public bool ForceHttp { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte HttpTimeout { get; set; } = 60;
|
||||
public byte HttpTimeout { get; set; } = DefaultHttpTimeout;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
[JsonProperty]
|
||||
public string WCFHostname { get; set; } = "localhost";
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ushort WCFPort { get; set; } = 1242;
|
||||
public ushort WCFPort { get; set; } = DefaultWCFPort;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool LogToFile { get; set; } = true;
|
||||
@@ -114,25 +123,51 @@ namespace ConfigGenerator {
|
||||
return new GlobalConfig(filePath);
|
||||
}
|
||||
|
||||
if (globalConfig == null) {
|
||||
return new GlobalConfig(filePath);
|
||||
}
|
||||
|
||||
globalConfig.FilePath = filePath;
|
||||
|
||||
// SK2 supports only TCP and UDP steam protocols
|
||||
// Make sure that user can't screw this up
|
||||
// Ensure that user can't screw this up
|
||||
switch (globalConfig.SteamProtocol) {
|
||||
case ProtocolType.Tcp:
|
||||
case ProtocolType.Udp:
|
||||
break;
|
||||
default:
|
||||
Logging.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol + ", default TCP protocol will be used instead");
|
||||
globalConfig.SteamProtocol = ProtocolType.Tcp;
|
||||
Logging.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol + ". Value of " + DefaultSteamProtocol + " will be used instead");
|
||||
globalConfig.SteamProtocol = DefaultSteamProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
// User might not know what he's doing
|
||||
// Ensure that he can't screw core ASF variables
|
||||
if (globalConfig.MaxFarmingTime == 0) {
|
||||
Logging.LogGenericWarning("Configured MaxFarmingTime is invalid: " + globalConfig.MaxFarmingTime + ". Value of " + DefaultMaxFarmingTime + " will be used instead");
|
||||
globalConfig.MaxFarmingTime = DefaultMaxFarmingTime;
|
||||
}
|
||||
|
||||
if (globalConfig.FarmingDelay == 0) {
|
||||
Logging.LogGenericWarning("Configured FarmingDelay is invalid: " + globalConfig.FarmingDelay + ". Value of " + DefaultFarmingDelay + " will be used instead");
|
||||
globalConfig.FarmingDelay = DefaultFarmingDelay;
|
||||
}
|
||||
|
||||
if (globalConfig.HttpTimeout == 0) {
|
||||
Logging.LogGenericWarning("Configured HttpTimeout is invalid: " + globalConfig.HttpTimeout + ". Value of " + DefaultHttpTimeout + " will be used instead");
|
||||
globalConfig.HttpTimeout = DefaultHttpTimeout;
|
||||
}
|
||||
|
||||
if (globalConfig.WCFPort == 0) {
|
||||
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
|
||||
globalConfig.WCFPort = DefaultWCFPort;
|
||||
}
|
||||
|
||||
return globalConfig;
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private GlobalConfig() : base() { }
|
||||
private GlobalConfig() { }
|
||||
|
||||
private GlobalConfig(string filePath) : base(filePath) {
|
||||
FilePath = filePath;
|
||||
|
||||
16
ConfigGenerator/MainForm.Designer.cs
generated
16
ConfigGenerator/MainForm.Designer.cs
generated
@@ -32,28 +32,30 @@
|
||||
this.MainTab.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.MainTab.Appearance = System.Windows.Forms.TabAppearance.Buttons;
|
||||
this.MainTab.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.MainTab.HotTrack = true;
|
||||
this.MainTab.Location = new System.Drawing.Point(16, 13);
|
||||
this.MainTab.Location = new System.Drawing.Point(14, 14);
|
||||
this.MainTab.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.MainTab.Multiline = true;
|
||||
this.MainTab.Name = "MainTab";
|
||||
this.MainTab.SelectedIndex = 0;
|
||||
this.MainTab.Size = new System.Drawing.Size(748, 529);
|
||||
this.MainTab.Size = new System.Drawing.Size(854, 745);
|
||||
this.MainTab.SizeMode = System.Windows.Forms.TabSizeMode.Fixed;
|
||||
this.MainTab.TabIndex = 1;
|
||||
this.MainTab.Selected += new System.Windows.Forms.TabControlEventHandler(this.MainTab_Selected);
|
||||
this.MainTab.Deselecting += new System.Windows.Forms.TabControlCancelEventHandler(this.MainTab_Deselecting);
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.AutoScroll = true;
|
||||
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.ClientSize = new System.Drawing.Size(780, 557);
|
||||
this.ClientSize = new System.Drawing.Size(882, 774);
|
||||
this.Controls.Add(this.MainTab);
|
||||
this.Cursor = System.Windows.Forms.Cursors.Default;
|
||||
this.DoubleBuffered = true;
|
||||
this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
|
||||
this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.HelpButton = true;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
@@ -31,20 +32,11 @@ namespace ConfigGenerator {
|
||||
public partial class MainForm : Form {
|
||||
private const byte ReservedTabs = 3;
|
||||
|
||||
private readonly TabPage NewTab = new TabPage { Text = "+" };
|
||||
private readonly TabPage RemoveTab = new TabPage { Text = "-" };
|
||||
private readonly TabPage RenameTab = new TabPage { Text = "~" };
|
||||
|
||||
private ConfigPage ASFTab;
|
||||
|
||||
private TabPage RemoveTab = new TabPage() {
|
||||
Text = "-",
|
||||
};
|
||||
|
||||
private TabPage RenameTab = new TabPage() {
|
||||
Text = "~",
|
||||
};
|
||||
|
||||
private TabPage NewTab = new TabPage() {
|
||||
Text = "+",
|
||||
};
|
||||
|
||||
private TabPage OldTab;
|
||||
|
||||
public MainForm() {
|
||||
@@ -73,7 +65,7 @@ namespace ConfigGenerator {
|
||||
Tutorial.Enabled = false;
|
||||
}
|
||||
|
||||
MainTab.TabPages.AddRange(new TabPage[] { RemoveTab, RenameTab, NewTab });
|
||||
MainTab.TabPages.AddRange(new[] { RemoveTab, RenameTab, NewTab });
|
||||
Tutorial.OnAction(Tutorial.EPhase.Start);
|
||||
}
|
||||
|
||||
@@ -186,7 +178,7 @@ namespace ConfigGenerator {
|
||||
Tutorial.OnAction(Tutorial.EPhase.Shown);
|
||||
}
|
||||
|
||||
private void MainForm_HelpButtonClicked(object sender, System.ComponentModel.CancelEventArgs e) {
|
||||
private void MainForm_HelpButtonClicked(object sender, CancelEventArgs e) {
|
||||
if (sender == null || e == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,10 +36,7 @@ namespace ConfigGenerator {
|
||||
|
||||
private const string ASFDirectory = "ArchiSteamFarm";
|
||||
|
||||
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
|
||||
private static readonly string ExecutableFile = Assembly.Location;
|
||||
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
|
||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
|
||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
ArchiSteamFarm
|
||||
===================
|
||||
|
||||
[](https://ci.appveyor.com/project/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://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)
|
||||
[](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://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)
|
||||
|
||||
---
|
||||
|
||||
|
||||
66
cc.sh
Executable file
66
cc.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
BUILD="Release"
|
||||
CLEAN=0
|
||||
|
||||
MONO_ARGS=("--aot" "--llvm" "--server" "-O=all")
|
||||
XBUILD_ARGS=("/nologo")
|
||||
BINARIES=("ArchiSteamFarm/bin/Release/ArchiSteamFarm.exe")
|
||||
SOLUTION="ArchiSteamFarm.sln"
|
||||
|
||||
PRINT_USAGE() {
|
||||
echo "Usage: $0 [--clean] [debug/release]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
for ARG in "$@"; do
|
||||
case "$ARG" in
|
||||
release|Release) BUILD="Release" ;;
|
||||
debug|Debug) BUILD="Debug" ;;
|
||||
--clean) CLEAN=1 ;;
|
||||
*) PRINT_USAGE
|
||||
esac
|
||||
done
|
||||
|
||||
XBUILD_ARGS+=("/p:Configuration=$BUILD")
|
||||
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
if [[ -d ".git" ]] && hash git &>/dev/null; then
|
||||
git pull
|
||||
fi
|
||||
|
||||
if [[ ! -f "$SOLUTION" ]]; then
|
||||
echo "ERROR: $SOLUTION could not be found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if hash nuget &>/dev/null; then
|
||||
nuget restore "$SOLUTION"
|
||||
fi
|
||||
|
||||
if [[ "$CLEAN" -eq 1 ]]; then
|
||||
rm -rf out
|
||||
xbuild "${XBUILD_ARGS[@]}" "/t:Clean" "$SOLUTION"
|
||||
fi
|
||||
|
||||
xbuild "${XBUILD_ARGS[@]}" "$SOLUTION"
|
||||
|
||||
if [[ ! -f "${BINARIES[0]}" ]]; then
|
||||
echo "ERROR: ${BINARIES[0]} binary could not be found!"
|
||||
fi
|
||||
|
||||
# If it's release build, use Mono AOT for output binaries
|
||||
if [[ "$BUILD" = "Release" ]]; then
|
||||
for BINARY in "${BINARIES[@]}"; do
|
||||
if [[ ! -f "$BINARY" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
mono "${MONO_ARGS[@]}" "$BINARY"
|
||||
done
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Compilation finished successfully! :)"
|
||||
28
run.sh
Executable file
28
run.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
BUILD="Release"
|
||||
|
||||
MONO_ARGS=("--llvm" "--server" "-O=all")
|
||||
|
||||
PRINT_USAGE() {
|
||||
echo "Usage: $0 [debug/release]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
for ARG in "$@"; do
|
||||
case "$ARG" in
|
||||
release|Release) BUILD="Release" ;;
|
||||
debug|Debug) BUILD="Debug" ;;
|
||||
*) PRINT_USAGE
|
||||
esac
|
||||
done
|
||||
|
||||
BINARY="ArchiSteamFarm/bin/$BUILD/ArchiSteamFarm.exe"
|
||||
|
||||
if [[ ! -f "$BINARY" ]]; then
|
||||
echo "ERROR: $BINARY could not be found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mono "${MONO_ARGS[@]}" "$BINARY"
|
||||
Binary file not shown.
BIN
tools/ILRepack/BamlParser.dll
Normal file
BIN
tools/ILRepack/BamlParser.dll
Normal file
Binary file not shown.
BIN
tools/ILRepack/Fasterflect.dll
Normal file
BIN
tools/ILRepack/Fasterflect.dll
Normal file
Binary file not shown.
BIN
tools/ILRepack/ILRepack.exe
Normal file
BIN
tools/ILRepack/ILRepack.exe
Normal file
Binary file not shown.
BIN
tools/ILRepack/Mono.Cecil.Mdb.dll
Normal file
BIN
tools/ILRepack/Mono.Cecil.Mdb.dll
Normal file
Binary file not shown.
BIN
tools/ILRepack/Mono.Cecil.Pdb.dll
Normal file
BIN
tools/ILRepack/Mono.Cecil.Pdb.dll
Normal file
Binary file not shown.
BIN
tools/ILRepack/Mono.Cecil.dll
Normal file
BIN
tools/ILRepack/Mono.Cecil.dll
Normal file
Binary file not shown.
BIN
tools/ILRepack/Mono.Posix.dll
Normal file
BIN
tools/ILRepack/Mono.Posix.dll
Normal file
Binary file not shown.
17
tools/NetHook2/README.md
Normal file
17
tools/NetHook2/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
NetHook2
|
||||
===================
|
||||
|
||||
This tool is used for reverse-engineering of Steam client. It's capable of hooking and recording network traffic sent/received by the client. If you're not trying to implement missing SK2 functionality in ASF, then please do not proceed.
|
||||
|
||||
1. Launch Steam client
|
||||
2. Execute ```hook.bat```
|
||||
3. Reproduce the functionality you're trying to add
|
||||
4. Execute ```unhook.bat```
|
||||
5. Use ```NetHookAnalyzer2.exe``` for analyzing recorded log (which can be found in your Steam directory)
|
||||
|
||||
- Source of the ```NetHook2.dll``` can be found **[here](https://github.com/SteamRE/SteamKit/tree/master/Resources/NetHook2)**
|
||||
- Source of the ```NetHookAnalyzer2.exe``` can be found **[here](https://github.com/SteamRE/SteamKit/tree/master/Resources/NetHookAnalyzer2)**
|
||||
|
||||
===================
|
||||
|
||||
There is absolutely no guarantee that this will even work for you, not to mention the consequences from hooking the external DLL into steam client. You're on your own.
|
||||
Reference in New Issue
Block a user