mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-30 21:20:46 +00:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91a96990aa | ||
|
|
abff9bd28d | ||
|
|
e2a8c14a0d | ||
|
|
6489502be7 | ||
|
|
f6e7c06141 | ||
|
|
3548411cda | ||
|
|
43ce5eb257 | ||
|
|
2756a2ebc7 | ||
|
|
d3470624bc | ||
|
|
0439656499 | ||
|
|
dc1ae32d37 | ||
|
|
b3b07a465b | ||
|
|
e6fff8c6bd | ||
|
|
b010009a2d | ||
|
|
444eb97f49 | ||
|
|
fbbe3c1d09 | ||
|
|
69bf0022d5 | ||
|
|
ac6e9bc15a | ||
|
|
1df2c3b042 | ||
|
|
160bfd612f | ||
|
|
feb80fbf49 | ||
|
|
5a267eb225 | ||
|
|
ccfad31053 | ||
|
|
bd3dfa3664 | ||
|
|
18c6fd639a | ||
|
|
75e3ef9818 | ||
|
|
44b401aabd | ||
|
|
4d6f2811bb | ||
|
|
11d4430bbc | ||
|
|
0cea70ef6a | ||
|
|
e4df12fc5b | ||
|
|
19818f126f | ||
|
|
f2a2f93273 | ||
|
|
e6d4304ca1 | ||
|
|
e020f54753 | ||
|
|
cada3fa920 | ||
|
|
2c99c4b433 | ||
|
|
a25a181cd9 | ||
|
|
5f3a6d4c10 | ||
|
|
3e82c9f11e | ||
|
|
bed87c4c80 | ||
|
|
37f5f82cf8 | ||
|
|
ff00b3049a | ||
|
|
b316d3b03a | ||
|
|
3f735d165c | ||
|
|
abde04cc83 | ||
|
|
e606ca05ed | ||
|
|
e0dbd70b37 | ||
|
|
d37f5bb250 | ||
|
|
fdd9744556 | ||
|
|
3814bd64f0 | ||
|
|
98679545d3 | ||
|
|
49b1259817 | ||
|
|
0c14128b63 | ||
|
|
c115158279 | ||
|
|
2a61ecb681 | ||
|
|
d06fbda6d6 | ||
|
|
7ba2e829d3 | ||
|
|
58ff2a2a4d | ||
|
|
d6c9fe3cde | ||
|
|
4b782bd10d | ||
|
|
1a1e48a33d | ||
|
|
f7d7c559b8 | ||
|
|
602cda73b7 | ||
|
|
f579462f60 | ||
|
|
8066f1a7c0 | ||
|
|
f8409e1be6 | ||
|
|
c36eeb8c28 | ||
|
|
d0344a7ab9 | ||
|
|
2c767bfe85 | ||
|
|
2816ecaa90 | ||
|
|
214746bca2 |
@@ -6,11 +6,18 @@ git:
|
||||
|
||||
mono:
|
||||
- weekly
|
||||
# - alpha
|
||||
# - beta
|
||||
- latest
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- mono: weekly
|
||||
# - mono: alpha
|
||||
# - mono: beta
|
||||
|
||||
before_script:
|
||||
- source mono_envsetup.sh
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
@@ -30,7 +30,6 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.JSON;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class ASF {
|
||||
@@ -75,29 +74,16 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Logging.LogGenericInfo("Checking new version...");
|
||||
|
||||
string response = await Program.WebBrowser.UrlGetToContentRetry(releaseURL).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(response)) {
|
||||
Logging.LogGenericWarning("Could not check latest version!");
|
||||
return;
|
||||
}
|
||||
|
||||
GitHub.ReleaseResponse releaseResponse;
|
||||
|
||||
if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
|
||||
try {
|
||||
releaseResponse = JsonConvert.DeserializeObject<GitHub.ReleaseResponse>(response);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e);
|
||||
releaseResponse = await Program.WebBrowser.UrlGetToJsonResultRetry<GitHub.ReleaseResponse>(releaseURL).ConfigureAwait(false);
|
||||
if (releaseResponse == null) {
|
||||
Logging.LogGenericWarning("Could not check latest version!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
List<GitHub.ReleaseResponse> releases;
|
||||
try {
|
||||
releases = JsonConvert.DeserializeObject<List<GitHub.ReleaseResponse>>(response);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
List<GitHub.ReleaseResponse> releases = await Program.WebBrowser.UrlGetToJsonResultRetry<List<GitHub.ReleaseResponse>>(releaseURL).ConfigureAwait(false);
|
||||
if ((releases == null) || (releases.Count == 0)) {
|
||||
Logging.LogGenericWarning("Could not check latest version!");
|
||||
return;
|
||||
|
||||
@@ -75,10 +75,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
Notifications = new HashSet<ENotification>();
|
||||
foreach (CMsgClientUserNotifications.Notification notification in msg.notifications) {
|
||||
Notifications.Add((ENotification) notification.user_notification_type);
|
||||
}
|
||||
Notifications = new HashSet<ENotification>(msg.notifications.Select(notification => (ENotification) notification.user_notification_type));
|
||||
}
|
||||
|
||||
internal NotificationsCallback(JobID jobID, CMsgClientItemAnnouncements msg) {
|
||||
|
||||
@@ -38,8 +38,9 @@
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>3</WarningLevel>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -53,7 +54,7 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
@@ -61,7 +62,7 @@
|
||||
<ApplicationIcon>cirno.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
@@ -95,18 +96,14 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -191,11 +188,15 @@
|
||||
copy "$(TargetDir)$(TargetName).exe" "$(SolutionDir)out\ASF.exe"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
if [ -f "$(SolutionDir)mono_envsetup.sh" ]; then
|
||||
. "$(SolutionDir)mono_envsetup.sh"
|
||||
fi
|
||||
|
||||
mkdir -p "$(SolutionDir)out/config"
|
||||
cp "$(TargetDir)config/ASF.json" "$(SolutionDir)out/config"
|
||||
cp "$(TargetDir)config/example.json" "$(SolutionDir)out/config"
|
||||
cp "$(TargetDir)config/minimal.json" "$(SolutionDir)out/config"
|
||||
mono --llvm --server -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-Service.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
mono "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-Service.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
rm "$(SolutionDir)out/ASF-Service.exe.config"
|
||||
cp "$(SolutionDir)out/ASF-Service.exe" "$(SolutionDir)out/ASF.exe"
|
||||
</PostBuildEvent>
|
||||
|
||||
@@ -322,22 +322,8 @@ namespace ArchiSteamFarm {
|
||||
|
||||
string request = SteamCommunityURL + "/mobileconf/details/" + confirmation.ID + "?l=english&p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf";
|
||||
|
||||
string json = await WebBrowser.UrlGetToContentRetry(request).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Steam.ConfirmationDetails response;
|
||||
|
||||
try {
|
||||
response = JsonConvert.DeserializeObject<Steam.ConfirmationDetails>(json);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Logging.LogNullError(nameof(response), Bot.BotName);
|
||||
Steam.ConfirmationDetails response = await WebBrowser.UrlGetToJsonResultRetry<Steam.ConfirmationDetails>(request).ConfigureAwait(false);
|
||||
if ((response == null) || !response.Success) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -345,14 +331,30 @@ namespace ArchiSteamFarm {
|
||||
return response;
|
||||
}
|
||||
|
||||
internal async Task<bool> HandleConfirmations(string deviceID, string confirmationHash, uint time, HashSet<MobileAuthenticator.Confirmation> confirmations, bool accept) {
|
||||
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmations == null) || (confirmations.Count == 0)) {
|
||||
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmations), Bot.BotName);
|
||||
return false;
|
||||
internal async Task<bool?> HandleConfirmation(string deviceID, string confirmationHash, uint time, uint confirmationID, ulong confirmationKey, bool accept) {
|
||||
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmationID == 0) || (confirmationKey == 0)) {
|
||||
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmationID) + " || " + nameof(confirmationKey), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/mobileconf/ajaxop?op=" + (accept ? "allow" : "cancel") + "&p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf&cid=" + confirmationID + "&ck=" + confirmationKey;
|
||||
|
||||
Steam.ConfirmationResponse response = await WebBrowser.UrlGetToJsonResultRetry<Steam.ConfirmationResponse>(request).ConfigureAwait(false);
|
||||
return response?.Success;
|
||||
}
|
||||
|
||||
internal async Task<bool?> HandleConfirmations(string deviceID, string confirmationHash, uint time, HashSet<MobileAuthenticator.Confirmation> confirmations, bool accept) {
|
||||
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmations == null) || (confirmations.Count == 0)) {
|
||||
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmations), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/mobileconf/multiajaxop";
|
||||
@@ -372,26 +374,8 @@ namespace ArchiSteamFarm {
|
||||
data.Add(new KeyValuePair<string, string>("ck[]", confirmation.Key.ToString()));
|
||||
}
|
||||
|
||||
string json = await WebBrowser.UrlPostToContentRetry(request, data).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Steam.ConfirmationResponse response;
|
||||
|
||||
try {
|
||||
response = JsonConvert.DeserializeObject<Steam.ConfirmationResponse>(json);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
return response.Success;
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(response), Bot.BotName);
|
||||
return false;
|
||||
Steam.ConfirmationResponse response = await WebBrowser.UrlPostToJsonResultRetry<Steam.ConfirmationResponse>(request, data).ConfigureAwait(false);
|
||||
return response?.Success;
|
||||
}
|
||||
|
||||
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
|
||||
@@ -725,7 +709,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
HashSet<Steam.Item> result = new HashSet<Steam.Item>();
|
||||
|
||||
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?l=english&trading=" + (tradable ? "1" : "0") + "&start=";
|
||||
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamCommunityContextID + "?l=english&trading=" + (tradable ? "1" : "0") + "&start=";
|
||||
uint currentPage = 0;
|
||||
|
||||
while (true) {
|
||||
@@ -862,7 +846,7 @@ namespace ArchiSteamFarm {
|
||||
itemID = 0;
|
||||
}
|
||||
|
||||
singleTrade.ItemsToGive.Assets.Add(new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamContextID, item.AssetID, item.Amount));
|
||||
singleTrade.ItemsToGive.Assets.Add(new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, item.AssetID, item.Amount));
|
||||
itemID++;
|
||||
}
|
||||
|
||||
@@ -937,24 +921,22 @@ namespace ArchiSteamFarm {
|
||||
|
||||
await SessionSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||
try {
|
||||
if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
||||
if (isLoggedIn.GetValueOrDefault(true)) {
|
||||
LastSessionRefreshCheck = DateTime.Now;
|
||||
return true;
|
||||
} else {
|
||||
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
|
||||
return await Bot.RefreshSession().ConfigureAwait(false);
|
||||
}
|
||||
} finally {
|
||||
SessionSemaphore.Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
||||
if (isLoggedIn.GetValueOrDefault(true)) {
|
||||
result = true;
|
||||
LastSessionRefreshCheck = DateTime.Now;
|
||||
} else {
|
||||
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
|
||||
result = await Bot.RefreshSession().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SessionSemaphore.Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<bool> UnlockParentalAccount(string parentalPin) {
|
||||
|
||||
@@ -41,11 +41,11 @@ using SteamKit2.Discovery;
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class Bot : IDisposable {
|
||||
private const ushort CallbackSleep = 500; // In miliseconds
|
||||
private const uint LoginID = 0; // This must be the same for all ASF bots and all ASF processes
|
||||
private const ushort MaxSteamMessageLength = 2048;
|
||||
|
||||
internal static readonly ConcurrentDictionary<string, Bot> Bots = new ConcurrentDictionary<string, Bot>();
|
||||
|
||||
private static readonly uint LoginID = MsgClientLogon.ObfuscationMask; // This must be the same for all ASF bots and all ASF processes
|
||||
private static readonly SemaphoreSlim GiftsSemaphore = new SemaphoreSlim(1);
|
||||
private static readonly SemaphoreSlim LoginSemaphore = new SemaphoreSlim(1);
|
||||
|
||||
@@ -53,7 +53,6 @@ namespace ArchiSteamFarm {
|
||||
internal readonly ArchiHandler ArchiHandler;
|
||||
internal readonly ArchiWebHandler ArchiWebHandler;
|
||||
internal readonly BotConfig BotConfig;
|
||||
internal readonly SteamClient SteamClient;
|
||||
|
||||
private readonly string SentryFile;
|
||||
private readonly BotDatabase BotDatabase;
|
||||
@@ -65,18 +64,22 @@ namespace ArchiSteamFarm {
|
||||
private readonly ConcurrentHashSet<ulong> HandledGifts = new ConcurrentHashSet<ulong>();
|
||||
private readonly ConcurrentHashSet<uint> OwnedPackageIDs = new ConcurrentHashSet<uint>();
|
||||
private readonly SteamApps SteamApps;
|
||||
private readonly SteamClient SteamClient;
|
||||
private readonly SteamFriends SteamFriends;
|
||||
private readonly SteamUser SteamUser;
|
||||
private readonly Timer AcceptConfirmationsTimer, HeartBeatTimer, SendItemsTimer;
|
||||
private readonly Trading Trading;
|
||||
|
||||
internal bool IsConnectedAndLoggedOn => SteamClient.IsConnected && (SteamClient.SteamID != null);
|
||||
|
||||
[JsonProperty]
|
||||
internal bool KeepRunning { get; private set; }
|
||||
|
||||
internal bool PlayingBlocked { get; private set; }
|
||||
|
||||
private bool FirstTradeSent, InvalidPassword, SkipFirstShutdown;
|
||||
private bool FirstTradeSent, SkipFirstShutdown;
|
||||
private string AuthCode, TwoFactorCode;
|
||||
private EResult LastLogOnResult;
|
||||
|
||||
internal static string GetAPIStatus() {
|
||||
var response = new {
|
||||
@@ -141,7 +144,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (Bots.ContainsKey(botName)) {
|
||||
throw new Exception("That bot is already defined!");
|
||||
throw new ArgumentException("That bot is already defined!");
|
||||
}
|
||||
|
||||
string botPath = Path.Combine(SharedInfo.ConfigDirectory, botName);
|
||||
@@ -183,10 +186,12 @@ namespace ArchiSteamFarm {
|
||||
// Initialize
|
||||
SteamClient = new SteamClient(Program.GlobalConfig.SteamProtocol);
|
||||
|
||||
if (Program.GlobalConfig.Debug && !Debugging.NetHookAlreadyInitialized && Directory.Exists(SharedInfo.DebugDirectory)) {
|
||||
if (Program.GlobalConfig.Debug && Directory.Exists(SharedInfo.DebugDirectory)) {
|
||||
string debugListenerPath = Path.Combine(SharedInfo.DebugDirectory, botName);
|
||||
|
||||
try {
|
||||
Debugging.NetHookAlreadyInitialized = true;
|
||||
SteamClient.DebugNetworkListener = new NetHookNetworkListener(SharedInfo.DebugDirectory);
|
||||
Directory.CreateDirectory(debugListenerPath);
|
||||
SteamClient.DebugNetworkListener = new NetHookNetworkListener(debugListenerPath);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e, botName);
|
||||
}
|
||||
@@ -255,7 +260,9 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// Register bot as available for ASF
|
||||
Bots[botName] = this;
|
||||
if (!Bots.TryAdd(botName, this)) {
|
||||
throw new ArgumentException("That bot is already defined!");
|
||||
}
|
||||
|
||||
if (!BotConfig.StartOnLaunch) {
|
||||
return;
|
||||
@@ -321,7 +328,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
internal async Task<bool> RefreshSession() {
|
||||
if (!SteamClient.IsConnected) {
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -357,14 +364,14 @@ namespace ArchiSteamFarm {
|
||||
KeepRunning = false;
|
||||
|
||||
if (SteamClient.IsConnected) {
|
||||
SteamClient.Disconnect();
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
Events.OnBotShutdown();
|
||||
}
|
||||
|
||||
internal async Task LootIfNeeded() {
|
||||
if (!BotConfig.SendOnFarmingFinished || (BotConfig.SteamMasterID == 0) || !SteamClient.IsConnected || (BotConfig.SteamMasterID == SteamClient.SteamID)) {
|
||||
if (!BotConfig.SendOnFarmingFinished || (BotConfig.SteamMasterID == 0) || !IsConnectedAndLoggedOn || (BotConfig.SteamMasterID == SteamClient.SteamID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -401,7 +408,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await ResponseRedeem(steamID, message, true).ConfigureAwait(false);
|
||||
return await ResponseRedeem(steamID, message, true, false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (message.IndexOf(' ') < 0) {
|
||||
@@ -487,10 +494,16 @@ namespace ArchiSteamFarm {
|
||||
return await ResponsePlay(steamID, BotName, args[1]).ConfigureAwait(false);
|
||||
case "!REDEEM":
|
||||
if (args.Length > 2) {
|
||||
return await ResponseRedeem(steamID, args[1], args[2], false).ConfigureAwait(false);
|
||||
return await ResponseRedeem(steamID, args[1], args[2], false, false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return await ResponseRedeem(steamID, BotName, args[1], false).ConfigureAwait(false);
|
||||
return await ResponseRedeem(steamID, BotName, args[1], false, false).ConfigureAwait(false);
|
||||
case "!REDEEM^":
|
||||
if (args.Length > 2) {
|
||||
return await ResponseRedeem(steamID, args[1], args[2], false, true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return await ResponseRedeem(steamID, BotName, args[1], false, true).ConfigureAwait(false);
|
||||
case "!RESUME":
|
||||
return await ResponsePause(steamID, args[1], false).ConfigureAwait(false);
|
||||
case "!START":
|
||||
@@ -505,28 +518,47 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private async Task HeartBeat() {
|
||||
if (!SteamClient.IsConnected) {
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await SteamApps.PICSGetProductInfo(0, null);
|
||||
} catch {
|
||||
if (!SteamClient.IsConnected) {
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericWarning("Connection to Steam Network lost, reconnecting...", BotName);
|
||||
Connect(true).Forget();
|
||||
}
|
||||
}
|
||||
|
||||
Task.Run(async () => {
|
||||
await LimitLoginRequestsAsync().ConfigureAwait(false);
|
||||
private async Task Connect(bool force = false) {
|
||||
if (!force && (!KeepRunning || SteamClient.IsConnected)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SteamClient.IsConnected) {
|
||||
return;
|
||||
}
|
||||
// Use limiter only when user is not providing 2FA token by himself
|
||||
if (string.IsNullOrEmpty(TwoFactorCode) && (BotDatabase.MobileAuthenticator != null)) {
|
||||
await LimitLoginRequestsAsync().ConfigureAwait(false);
|
||||
|
||||
SteamClient.Connect();
|
||||
}).Forget();
|
||||
// In this case, we can also use ASF 2FA for providing 2FA token, even if it's not required
|
||||
TwoFactorCode = await BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
lock (SteamClient) {
|
||||
if (!force && (!KeepRunning || SteamClient.IsConnected)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SteamClient.Connect();
|
||||
}
|
||||
}
|
||||
|
||||
private void Disconnect() {
|
||||
lock (SteamClient) {
|
||||
SteamClient.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,8 +569,7 @@ namespace ArchiSteamFarm {
|
||||
Logging.LogGenericInfo("Starting...", BotName);
|
||||
}
|
||||
|
||||
await LimitLoginRequestsAsync().ConfigureAwait(false);
|
||||
SteamClient.Connect();
|
||||
await Connect().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private bool IsMaster(ulong steamID) {
|
||||
@@ -634,6 +665,10 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return "This bot instance is not connected!";
|
||||
}
|
||||
|
||||
if (pause) {
|
||||
if (CardsFarmer.ManualMode) {
|
||||
return "Automatic farming is stopped already!";
|
||||
@@ -679,7 +714,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!SteamClient.IsConnected) {
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
if (KeepRunning) {
|
||||
return "Bot " + BotName + " is not connected.";
|
||||
}
|
||||
@@ -764,6 +799,10 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return "This bot instance is not connected!";
|
||||
}
|
||||
|
||||
if (BotConfig.SteamMasterID == 0) {
|
||||
return "Trade couldn't be send because SteamMasterID is not defined!";
|
||||
}
|
||||
@@ -780,10 +819,10 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// Remove from our pending inventory all items that are not steam cards and boosters
|
||||
inventory.RemoveWhere(item => (item.Type != Steam.Item.EType.TradingCard) && (item.Type != Steam.Item.EType.FoilTradingCard) && (item.Type != Steam.Item.EType.BoosterPack));
|
||||
|
||||
if (inventory.Count == 0) {
|
||||
return "Nothing to send, inventory seems empty!";
|
||||
if (inventory.RemoveWhere(item => (item.Type != Steam.Item.EType.TradingCard) && ((item.Type != Steam.Item.EType.FoilTradingCard) || !BotConfig.IsBotAccount) && (item.Type != Steam.Item.EType.BoosterPack)) > 0) {
|
||||
if (inventory.Count == 0) {
|
||||
return "Nothing to send, inventory seems empty!";
|
||||
}
|
||||
}
|
||||
|
||||
if (!await ArchiWebHandler.SendTradeOffer(inventory, BotConfig.SteamMasterID, BotConfig.SteamTradeToken).ConfigureAwait(false)) {
|
||||
@@ -823,7 +862,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
await Task.WhenAll(Bots.Values.Where(bot => bot.SteamClient.IsConnected).Select(bot => bot.ResponseLoot(steamID))).ConfigureAwait(false);
|
||||
await Task.WhenAll(Bots.Values.Where(bot => bot.IsConnectedAndLoggedOn).Select(bot => bot.ResponseLoot(steamID))).ConfigureAwait(false);
|
||||
return "Done!";
|
||||
}
|
||||
|
||||
@@ -841,8 +880,12 @@ namespace ArchiSteamFarm {
|
||||
return "That bot doesn't have ASF 2FA enabled!";
|
||||
}
|
||||
|
||||
byte timeLeft = (byte) (30 - await BotDatabase.MobileAuthenticator.GetSteamTime().ConfigureAwait(false) % 30);
|
||||
return "2FA Token: " + await BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false) + " (expires in " + timeLeft + " seconds)";
|
||||
string token = await BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(token)) {
|
||||
return "Error!";
|
||||
}
|
||||
|
||||
return "2FA Token: " + token;
|
||||
}
|
||||
|
||||
private static async Task<string> Response2FA(ulong steamID, string botName) {
|
||||
@@ -940,7 +983,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!SteamClient.IsConnected) {
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return "This bot instance is not connected!";
|
||||
}
|
||||
|
||||
@@ -980,7 +1023,7 @@ namespace ArchiSteamFarm {
|
||||
return "https://github.com/" + SharedInfo.GithubRepo + "/wiki/Commands";
|
||||
}
|
||||
|
||||
private async Task<string> ResponseRedeem(ulong steamID, string message, bool validate) {
|
||||
private async Task<string> ResponseRedeem(ulong steamID, string message, bool validate, bool skipForwarding) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(message)) {
|
||||
Logging.LogNullError(nameof(steamID) + " || " + nameof(message), BotName);
|
||||
return null;
|
||||
@@ -1003,7 +1046,7 @@ namespace ArchiSteamFarm {
|
||||
continue; // Keep current bot
|
||||
}
|
||||
|
||||
if (!currentBot.SteamClient.IsConnected) {
|
||||
if (!currentBot.IsConnectedAndLoggedOn) {
|
||||
currentBot = null; // Either bot will be changed, or loop aborted
|
||||
} else {
|
||||
ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
||||
@@ -1030,7 +1073,7 @@ namespace ArchiSteamFarm {
|
||||
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.RegionLocked:
|
||||
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + result.PurchaseResult + " | Items: " + string.Join("", result.Items));
|
||||
|
||||
if (!BotConfig.ForwardKeysToOtherBots) {
|
||||
if (skipForwarding || !BotConfig.ForwardKeysToOtherBots) {
|
||||
key = reader.ReadLine(); // Next key
|
||||
break; // Next bot (if needed)
|
||||
}
|
||||
@@ -1040,8 +1083,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
bool alreadyHandled = false;
|
||||
foreach (Bot bot in Bots.Where(bot => (bot.Value != this) && bot.Value.SteamClient.IsConnected && ((result.Items.Count == 0) || result.Items.Keys.Any(packageID => !bot.Value.OwnedPackageIDs.Contains(packageID)))).OrderBy(bot => bot.Key).Select(bot => bot.Value)) {
|
||||
|
||||
foreach (Bot bot in Bots.Where(bot => (bot.Value != this) && bot.Value.IsConnectedAndLoggedOn && ((result.Items.Count == 0) || result.Items.Keys.Any(packageID => !bot.Value.OwnedPackageIDs.Contains(packageID)))).OrderBy(bot => bot.Key).Select(bot => bot.Value)) {
|
||||
ArchiHandler.PurchaseResponseCallback otherResult = await bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
||||
if (otherResult == null) {
|
||||
response.Append(Environment.NewLine + "<" + bot.BotName + "> Key: " + key + " | Status: Timeout!");
|
||||
@@ -1062,11 +1104,7 @@ namespace ArchiSteamFarm {
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.Items.Count != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<uint, string> item in otherResult.Items) {
|
||||
foreach (KeyValuePair<uint, string> item in otherResult.Items.Where(item => !result.Items.ContainsKey(item.Key))) {
|
||||
result.Items[item.Key] = item.Value;
|
||||
}
|
||||
}
|
||||
@@ -1077,20 +1115,20 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
if (!BotConfig.DistributeKeys) {
|
||||
if (skipForwarding || !BotConfig.DistributeKeys) {
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
currentBot = iterator.MoveNext() ? iterator.Current : null;
|
||||
} while ((currentBot == this) || ((currentBot != null) && !currentBot.SteamClient.IsConnected));
|
||||
} while ((currentBot == this) || ((currentBot != null) && !currentBot.IsConnectedAndLoggedOn));
|
||||
}
|
||||
}
|
||||
|
||||
return response.Length == 0 ? null : response.ToString();
|
||||
}
|
||||
|
||||
private static async Task<string> ResponseRedeem(ulong steamID, string botName, string message, bool validate) {
|
||||
private static async Task<string> ResponseRedeem(ulong steamID, string botName, string message, bool validate, bool skipForwarding) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName) || string.IsNullOrEmpty(message)) {
|
||||
Logging.LogNullError(nameof(steamID) + " || " + nameof(botName) + " || " + nameof(message));
|
||||
return null;
|
||||
@@ -1098,7 +1136,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Bot bot;
|
||||
if (Bots.TryGetValue(botName, out bot)) {
|
||||
return await bot.ResponseRedeem(steamID, message, validate).ConfigureAwait(false);
|
||||
return await bot.ResponseRedeem(steamID, message, validate, skipForwarding).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (IsOwner(steamID)) {
|
||||
@@ -1150,10 +1188,14 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!SteamClient.IsConnected || !IsMaster(steamID)) {
|
||||
if (!IsMaster(steamID)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return "This bot instance is not connected!";
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
foreach (uint gameID in gameIDs) {
|
||||
SteamApps.FreeLicenseCallback callback = await SteamApps.RequestFreeLicense(gameID);
|
||||
@@ -1212,6 +1254,10 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return "This bot instance is not connected!";
|
||||
}
|
||||
|
||||
Dictionary<uint, string> ownedGames;
|
||||
if (!string.IsNullOrEmpty(BotConfig.SteamApiKey)) {
|
||||
ownedGames = ArchiWebHandler.GetOwnedGames(SteamClient.SteamID);
|
||||
@@ -1220,7 +1266,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if ((ownedGames == null) || (ownedGames.Count == 0)) {
|
||||
return "<" + BotName + "> List of owned games is empty!";
|
||||
return Environment.NewLine + "<" + BotName + "> List of owned games is empty!";
|
||||
}
|
||||
|
||||
StringBuilder response = new StringBuilder();
|
||||
@@ -1250,7 +1296,7 @@ namespace ArchiSteamFarm {
|
||||
return response.ToString();
|
||||
}
|
||||
|
||||
return "<" + BotName + "> Not owned yet: " + query;
|
||||
return Environment.NewLine + "<" + BotName + "> Not owned yet: " + query;
|
||||
}
|
||||
|
||||
private static async Task<string> ResponseOwns(ulong steamID, string botName, string query) {
|
||||
@@ -1281,7 +1327,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
string[] responses = await Task.WhenAll(Bots.OrderBy(bot => bot.Key).Select(bot => bot.Value.ResponseOwns(steamID, query))).ConfigureAwait(false);
|
||||
string[] responses = await Task.WhenAll(Bots.Where(bot => bot.Value.IsConnectedAndLoggedOn).OrderBy(bot => bot.Key).Select(bot => bot.Value.ResponseOwns(steamID, query))).ConfigureAwait(false);
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
foreach (string response in responses.Where(response => !string.IsNullOrEmpty(response))) {
|
||||
@@ -1301,6 +1347,10 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return "This bot instance is not connected!";
|
||||
}
|
||||
|
||||
if (gameIDs.Contains(0)) {
|
||||
if (!CardsFarmer.ManualMode) {
|
||||
return "Done!";
|
||||
@@ -1509,7 +1559,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SteamClient.IsConnected) {
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1525,7 +1575,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SteamClient.IsConnected) {
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1536,7 +1586,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private void JoinMasterChat() {
|
||||
if (!SteamClient.IsConnected || (BotConfig.SteamMasterClanID == 0)) {
|
||||
if (!IsConnectedAndLoggedOn || (BotConfig.SteamMasterClanID == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1567,7 +1617,7 @@ namespace ArchiSteamFarm {
|
||||
ArchiHandler.PlayGames(BotConfig.GamesPlayedWhileIdle, BotConfig.CustomGamePlayedWhileIdle);
|
||||
}
|
||||
|
||||
private async void OnConnected(SteamClient.ConnectedCallback callback) {
|
||||
private void OnConnected(SteamClient.ConnectedCallback callback) {
|
||||
if (callback == null) {
|
||||
Logging.LogNullError(nameof(callback), BotName);
|
||||
return;
|
||||
@@ -1582,7 +1632,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
if (!KeepRunning) {
|
||||
Logging.LogGenericInfo("Disconnecting...", BotName);
|
||||
SteamClient.Disconnect();
|
||||
Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1603,11 +1653,6 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Logging.LogGenericInfo("Logging in...", BotName);
|
||||
|
||||
// If we have ASF 2FA enabled, we can always provide TwoFactorCode, and save a request
|
||||
if (BotDatabase.MobileAuthenticator != null) {
|
||||
TwoFactorCode = await BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SteamUser.LogOnDetails logOnDetails = new SteamUser.LogOnDetails {
|
||||
Username = BotConfig.SteamLogin,
|
||||
Password = BotConfig.SteamPassword,
|
||||
@@ -1634,6 +1679,9 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
EResult lastLogOnResult = LastLogOnResult;
|
||||
LastLogOnResult = EResult.Invalid;
|
||||
|
||||
Logging.LogGenericInfo("Disconnected from Steam!", BotName);
|
||||
|
||||
ArchiWebHandler.OnDisconnected();
|
||||
@@ -1648,12 +1696,17 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (InvalidPassword) {
|
||||
InvalidPassword = false;
|
||||
if (!string.IsNullOrEmpty(BotDatabase.LoginKey)) { // InvalidPassword means usually that login key has expired, if we used it
|
||||
BotDatabase.LoginKey = null;
|
||||
Logging.LogGenericInfo("Removed expired login key", BotName);
|
||||
} else { // If we didn't use login key, InvalidPassword usually means we got captcha or other network-based throttling
|
||||
if (lastLogOnResult != EResult.Invalid) {
|
||||
if (lastLogOnResult == EResult.InvalidPassword) {
|
||||
if (!string.IsNullOrEmpty(BotDatabase.LoginKey)) { // InvalidPassword means usually that login key has expired, if we used it
|
||||
BotDatabase.LoginKey = null;
|
||||
Logging.LogGenericInfo("Removed expired login key", BotName);
|
||||
} else { // If we didn't use login key, InvalidPassword usually means we got captcha or other network-based throttling
|
||||
lastLogOnResult = EResult.RateLimitExceeded;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastLogOnResult == EResult.RateLimitExceeded) {
|
||||
Logging.LogGenericInfo("Will retry after 25 minutes...", BotName);
|
||||
await Task.Delay(25 * 60 * 1000).ConfigureAwait(false); // Captcha disappears after around 20 minutes, so we make it 25
|
||||
}
|
||||
@@ -1664,17 +1717,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Reconnecting...", BotName);
|
||||
|
||||
// 2FA tokens are expiring soon, don't use limiter when user is providing one
|
||||
if ((TwoFactorCode == null) || (BotDatabase.MobileAuthenticator != null)) {
|
||||
await LimitLoginRequestsAsync().ConfigureAwait(false);
|
||||
|
||||
if (!KeepRunning || SteamClient.IsConnected) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SteamClient.Connect();
|
||||
await Connect().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void OnFreeLicense(SteamApps.FreeLicenseCallback callback) {
|
||||
@@ -1840,7 +1883,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// If message is too old, return
|
||||
if (DateTime.UtcNow.Subtract(lastMessage.Timestamp).TotalMinutes > 1) {
|
||||
if (DateTime.UtcNow.Subtract(lastMessage.Timestamp).TotalHours > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1879,6 +1922,13 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Logged off of Steam: " + callback.Result, BotName);
|
||||
|
||||
switch (callback.Result) {
|
||||
case EResult.LogonSessionReplaced:
|
||||
Logging.LogGenericError("This account seems to be used in another ASF instance, which is undefined behaviour, refusing to keep it running!", BotName);
|
||||
Stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnLoggedOn(SteamUser.LoggedOnCallback callback) {
|
||||
@@ -1887,9 +1937,12 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset one-time-only access tokens
|
||||
// Always reset one-time-only access tokens
|
||||
AuthCode = TwoFactorCode = null;
|
||||
|
||||
// Keep LastLogOnResult for OnDisconnected()
|
||||
LastLogOnResult = callback.Result;
|
||||
|
||||
switch (callback.Result) {
|
||||
case EResult.AccountLogonDenied:
|
||||
AuthCode = Program.GetUserInput(SharedInfo.EUserInputType.SteamGuard, BotName);
|
||||
@@ -1908,17 +1961,13 @@ namespace ArchiSteamFarm {
|
||||
Logging.LogGenericWarning("2FA code was invalid despite of using ASF 2FA. Invalid authenticator or bad timing?", BotName);
|
||||
}
|
||||
|
||||
break;
|
||||
case EResult.InvalidPassword:
|
||||
InvalidPassword = true;
|
||||
Logging.LogGenericWarning("Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult, BotName);
|
||||
break;
|
||||
case EResult.OK:
|
||||
Logging.LogGenericInfo("Successfully logged on!", BotName);
|
||||
|
||||
PlayingBlocked = false; // If playing is really blocked, we'll be notified in a callback, old status doesn't matter
|
||||
|
||||
if (callback.CellID != 0) {
|
||||
if ((callback.CellID != 0) && (Program.GlobalDatabase.CellID != callback.CellID)) {
|
||||
Program.GlobalDatabase.CellID = callback.CellID;
|
||||
}
|
||||
|
||||
@@ -1961,7 +2010,9 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Trading.CheckTrades().Forget();
|
||||
break;
|
||||
case EResult.InvalidPassword:
|
||||
case EResult.NoConnection:
|
||||
case EResult.RateLimitExceeded:
|
||||
case EResult.ServiceUnavailable:
|
||||
case EResult.Timeout:
|
||||
case EResult.TryAnotherCM:
|
||||
|
||||
@@ -32,6 +32,7 @@ using System.Linq;
|
||||
namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "ConvertToConstant.Local")]
|
||||
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
|
||||
internal sealed class BotConfig {
|
||||
internal enum EFarmingOrder : byte {
|
||||
@@ -58,10 +59,6 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty]
|
||||
internal string SteamPassword { get; set; }
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
[SuppressMessage("ReSharper", "ConvertToConstant.Local")]
|
||||
private readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;
|
||||
|
||||
[JsonProperty]
|
||||
internal string SteamParentalPIN { get; set; } = "0";
|
||||
|
||||
@@ -128,6 +125,9 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly HashSet<uint> GamesPlayedWhileIdle = new HashSet<uint>();
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
private readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;
|
||||
|
||||
internal static BotConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
Logging.LogNullError(nameof(filePath));
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty]
|
||||
internal ushort CardsRemaining { get; set; }
|
||||
|
||||
internal string HeaderURL => "https://steamcdn-a.akamaihd.net/steam/apps/" + AppID + "/header.jpg";
|
||||
//internal string HeaderURL => "https://steamcdn-a.akamaihd.net/steam/apps/" + AppID + "/header.jpg";
|
||||
|
||||
internal Game(uint appID, string gameName, float hoursPlayed, ushort cardsRemaining) {
|
||||
if ((appID == 0) || string.IsNullOrEmpty(gameName) || (hoursPlayed < 0) || (cardsRemaining == 0)) {
|
||||
@@ -146,36 +146,36 @@ namespace ArchiSteamFarm {
|
||||
|
||||
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (NowFarming || ManualMode || Bot.PlayingBlocked) {
|
||||
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
||||
return;
|
||||
try {
|
||||
if (NowFarming || ManualMode || Bot.PlayingBlocked) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
|
||||
Logging.LogGenericInfo("We don't have anything to farm on this account!", Bot.BotName);
|
||||
await Bot.OnFarmingFinished(false).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("We have a total of " + GamesToFarm.Count + " games (" + GamesToFarm.Sum(game => game.CardsRemaining) + " cards) to farm on this account...", Bot.BotName);
|
||||
|
||||
// This is the last moment for final check if we can farm
|
||||
if (Bot.PlayingBlocked) {
|
||||
Logging.LogGenericInfo("But account is currently occupied, so farming is stopped!", Bot.BotName);
|
||||
return;
|
||||
}
|
||||
|
||||
KeepFarming = NowFarming = true;
|
||||
} finally {
|
||||
FarmingSemaphore.Release();
|
||||
}
|
||||
|
||||
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
|
||||
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
||||
Logging.LogGenericInfo("We don't have anything to farm on this account!", Bot.BotName);
|
||||
await Bot.OnFarmingFinished(false).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("We have a total of " + GamesToFarm.Count + " games (" + GamesToFarm.Sum(game => game.CardsRemaining) + " cards) to farm on this account...", Bot.BotName);
|
||||
|
||||
// This is the last moment for final check if we can farm
|
||||
if (Bot.PlayingBlocked) {
|
||||
Logging.LogGenericInfo("But account is currently occupied, so farming is stopped!", Bot.BotName);
|
||||
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
||||
return;
|
||||
}
|
||||
|
||||
KeepFarming = NowFarming = true;
|
||||
FarmingSemaphore.Release(); // From this point we allow other calls to shut us down
|
||||
|
||||
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<Game> gamesToFarmSolo = new HashSet<Game>(GamesToFarm.Where(game => game.HoursPlayed >= 2));
|
||||
HashSet<Game> gamesToFarmSolo = GamesToFarm.Count > 1 ? new HashSet<Game>(GamesToFarm.Where(game => game.HoursPlayed >= 2)) : new HashSet<Game>(GamesToFarm);
|
||||
if (gamesToFarmSolo.Count > 0) {
|
||||
while (gamesToFarmSolo.Count > 0) {
|
||||
Game game = gamesToFarmSolo.First();
|
||||
@@ -223,27 +223,29 @@ namespace ArchiSteamFarm {
|
||||
|
||||
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (!NowFarming) {
|
||||
try {
|
||||
if (!NowFarming) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Sending signal to stop farming", Bot.BotName);
|
||||
KeepFarming = false;
|
||||
FarmResetEvent.Set();
|
||||
|
||||
Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName);
|
||||
for (byte i = 0; (i < 5) && NowFarming; i++) {
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (NowFarming) {
|
||||
Logging.LogGenericWarning("Timed out!", Bot.BotName);
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Farming stopped!", Bot.BotName);
|
||||
Bot.OnFarmingStopped();
|
||||
} finally {
|
||||
FarmingSemaphore.Release();
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Sending signal to stop farming", Bot.BotName);
|
||||
KeepFarming = false;
|
||||
FarmResetEvent.Set();
|
||||
|
||||
Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName);
|
||||
for (byte i = 0; (i < 5) && NowFarming; i++) {
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (NowFarming) {
|
||||
Logging.LogGenericWarning("Timed out!", Bot.BotName);
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Farming stopped!", Bot.BotName);
|
||||
Bot.OnFarmingStopped();
|
||||
FarmingSemaphore.Release();
|
||||
}
|
||||
|
||||
internal void OnDisconnected() => StopFarming().Forget();
|
||||
@@ -503,7 +505,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private void CheckGamesForFarming() {
|
||||
if (NowFarming || ManualMode || !Bot.SteamClient.IsConnected) {
|
||||
if (NowFarming || ManualMode || !Bot.IsConnectedAndLoggedOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,21 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal bool ReplaceIfNeededWith(HashSet<T> items) {
|
||||
Lock.EnterUpgradeableReadLock();
|
||||
|
||||
try {
|
||||
if (HashSet.SetEquals(items)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReplaceWith(items);
|
||||
return true;
|
||||
} finally {
|
||||
Lock.ExitUpgradeableReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
internal void ReplaceWith(IEnumerable<T> items) {
|
||||
Lock.EnterWriteLock();
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
*/
|
||||
|
||||
using SteamKit2;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Debugging {
|
||||
@@ -37,33 +35,14 @@ namespace ArchiSteamFarm {
|
||||
internal static readonly bool IsDebugBuild = false;
|
||||
#endif
|
||||
|
||||
internal static bool NetHookAlreadyInitialized { get; set; }
|
||||
|
||||
internal sealed class DebugListener : IDebugListener {
|
||||
private readonly object FileLock = new object();
|
||||
private readonly string FilePath;
|
||||
|
||||
internal DebugListener(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
public void WriteLine(string category, string msg) {
|
||||
if (string.IsNullOrEmpty(category) && string.IsNullOrEmpty(msg)) {
|
||||
Logging.LogNullError(nameof(category) + " && " + nameof(msg));
|
||||
return;
|
||||
}
|
||||
|
||||
lock (FileLock) {
|
||||
try {
|
||||
File.AppendAllText(FilePath, category + " | " + msg + Environment.NewLine);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
Logging.LogGenericDebug(category + " | " + msg, nameof(DebugListener));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,14 +25,13 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class GlobalDatabase {
|
||||
internal sealed class GlobalDatabase : IDisposable {
|
||||
private static readonly JsonSerializerSettings CustomSerializerSettings = new JsonSerializerSettings {
|
||||
Converters = new List<JsonConverter> {
|
||||
Converters = new List<JsonConverter>(2) {
|
||||
new IPAddressConverter(),
|
||||
new IPEndPointConverter()
|
||||
}
|
||||
@@ -56,7 +55,6 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")]
|
||||
internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider();
|
||||
|
||||
private readonly object FileLock = new object();
|
||||
@@ -91,7 +89,10 @@ namespace ArchiSteamFarm {
|
||||
return globalDatabase;
|
||||
}
|
||||
|
||||
private void OnServerListUpdated(object sender, EventArgs e) => Save();
|
||||
public void Dispose() {
|
||||
ServerListProvider.ServerListUpdated -= OnServerListUpdated;
|
||||
ServerListProvider.Dispose();
|
||||
}
|
||||
|
||||
// This constructor is used when creating new database
|
||||
private GlobalDatabase(string filePath) : this() {
|
||||
@@ -104,11 +105,12 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
private GlobalDatabase() {
|
||||
ServerListProvider.ServerListUpdated += OnServerListUpdated;
|
||||
}
|
||||
|
||||
private void OnServerListUpdated(object sender, EventArgs e) => Save();
|
||||
|
||||
private void Save() {
|
||||
string json = JsonConvert.SerializeObject(this, CustomSerializerSettings);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
|
||||
@@ -24,36 +24,36 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using SteamKit2.Discovery;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class InMemoryServerListProvider : IServerListProvider {
|
||||
internal sealed class InMemoryServerListProvider : IDisposable, IServerListProvider {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
|
||||
private HashSet<IPEndPoint> Servers = new HashSet<IPEndPoint>();
|
||||
private readonly ConcurrentHashSet<IPEndPoint> Servers = new ConcurrentHashSet<IPEndPoint>();
|
||||
|
||||
internal event EventHandler ServerListUpdated = delegate { };
|
||||
|
||||
public Task<IEnumerable<IPEndPoint>> FetchServerListAsync() => Task.FromResult<IEnumerable<IPEndPoint>>(Servers);
|
||||
|
||||
public Task UpdateServerListAsync(IEnumerable<IPEndPoint> endpoints) {
|
||||
if (endpoints == null) {
|
||||
Logging.LogNullError(nameof(endpoints));
|
||||
public Task UpdateServerListAsync(IEnumerable<IPEndPoint> endPoints) {
|
||||
if (endPoints == null) {
|
||||
Logging.LogNullError(nameof(endPoints));
|
||||
return Task.Delay(0);
|
||||
}
|
||||
|
||||
Servers.Clear();
|
||||
foreach (IPEndPoint endpoint in endpoints) {
|
||||
Servers.Add(endpoint);
|
||||
HashSet<IPEndPoint> newServers = new HashSet<IPEndPoint>(endPoints);
|
||||
|
||||
if (!Servers.ReplaceIfNeededWith(newServers)) {
|
||||
return Task.Delay(0);
|
||||
}
|
||||
|
||||
ServerListUpdated(this, EventArgs.Empty);
|
||||
|
||||
return Task.Delay(0);
|
||||
}
|
||||
|
||||
public void Dispose() => Servers.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ using Newtonsoft.Json;
|
||||
namespace ArchiSteamFarm.JSON {
|
||||
internal static class GitHub {
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
|
||||
internal sealed class ReleaseResponse {
|
||||
#pragma warning disable 649
|
||||
internal sealed class Asset {
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace ArchiSteamFarm.JSON {
|
||||
internal static class Steam {
|
||||
internal sealed class Item { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset | Deserialized from JSON (SteamCommunity) and constructed from code
|
||||
internal const ushort SteamAppID = 753;
|
||||
internal const byte SteamContextID = 6;
|
||||
internal const byte SteamCommunityContextID = 6;
|
||||
|
||||
internal enum EType : byte {
|
||||
Unknown,
|
||||
@@ -269,9 +269,9 @@ namespace ArchiSteamFarm.JSON {
|
||||
State = state;
|
||||
}
|
||||
|
||||
internal bool IsSteamCardsOnlyTradeForUs() => ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamContextID) && (item.Type == Item.EType.TradingCard));
|
||||
internal bool IsSteamCardsRequest() => ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamCommunityContextID) && (item.Type == Item.EType.TradingCard));
|
||||
|
||||
internal bool IsPotentiallyDupesTradeForUs() {
|
||||
internal bool IsFairTypesExchange() {
|
||||
Dictionary<uint, Dictionary<Item.EType, uint>> itemsToGivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
|
||||
foreach (Item item in ItemsToGive) {
|
||||
Dictionary<Item.EType, uint> itemsPerType;
|
||||
@@ -343,7 +343,6 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
|
||||
internal sealed class ConfirmationResponse { // Deserialized from JSON
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(PropertyName = "success", Required = Required.Always)]
|
||||
@@ -355,7 +354,6 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
|
||||
internal sealed class ConfirmationDetails { // Deserialized from JSON
|
||||
internal enum EType : byte {
|
||||
Unknown,
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -35,7 +34,7 @@ using NLog.Targets;
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Logging {
|
||||
private const string LayoutMessage = @"${message}${onexception:inner= ${exception:format=toString,Data}}";
|
||||
private const string GeneralLayout = @"${date:format=yyyy-MM-dd HH\:mm\:ss}|${level:uppercase=true}|" + LayoutMessage;
|
||||
private const string GeneralLayout = @"${date:format=yyyy-MM-dd HH\:mm\:ss}|${processname}-${processid}|${level:uppercase=true}|" + LayoutMessage;
|
||||
private const string EventLogLayout = LayoutMessage;
|
||||
|
||||
private static readonly ConcurrentHashSet<LoggingRule> ConsoleLoggingRules = new ConcurrentHashSet<LoggingRule>();
|
||||
@@ -53,12 +52,12 @@ namespace ArchiSteamFarm {
|
||||
|
||||
LoggingConfiguration config = new LoggingConfiguration();
|
||||
|
||||
ColoredConsoleTarget consoleTarget = new ColoredConsoleTarget("Console") {
|
||||
ColoredConsoleTarget coloredConsoleTarget = new ColoredConsoleTarget("ColoredConsole") {
|
||||
Layout = GeneralLayout
|
||||
};
|
||||
|
||||
config.AddTarget(consoleTarget);
|
||||
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, consoleTarget));
|
||||
config.AddTarget(coloredConsoleTarget);
|
||||
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, coloredConsoleTarget));
|
||||
|
||||
if (Program.IsRunningAsService) {
|
||||
EventLogTarget eventLogTarget = new EventLogTarget("EventLog") {
|
||||
@@ -184,7 +183,6 @@ namespace ArchiSteamFarm {
|
||||
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal static void LogGenericDebug(string message, string botName = SharedInfo.ASF, [CallerMemberName] string previousMethodName = null) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
@@ -197,7 +195,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static void InitConsoleLoggers() {
|
||||
ConsoleLoggingRules.Clear();
|
||||
foreach (LoggingRule loggingRule in from loggingRule in LogManager.Configuration.LoggingRules from target in loggingRule.Targets where target is ColoredConsoleTarget || target is ConsoleTarget select loggingRule) {
|
||||
foreach (LoggingRule loggingRule in LogManager.Configuration.LoggingRules.Where(loggingRule => loggingRule.Targets.Any(target => target is ColoredConsoleTarget || target is ConsoleTarget))) {
|
||||
ConsoleLoggingRules.Add(loggingRule);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ using Newtonsoft.Json;
|
||||
namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
internal sealed class MobileAuthenticator {
|
||||
internal sealed class MobileAuthenticator : IDisposable {
|
||||
internal sealed class Confirmation {
|
||||
internal readonly uint ID;
|
||||
internal readonly ulong Key;
|
||||
@@ -64,9 +64,9 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static readonly SemaphoreSlim TimeSemaphore = new SemaphoreSlim(1);
|
||||
|
||||
private static short SteamTimeDifference;
|
||||
private static short? SteamTimeDifference;
|
||||
|
||||
internal bool HasCorrectDeviceID => !string.IsNullOrEmpty(DeviceID) && !DeviceID.Equals("ERROR"); // "ERROR" is being used by SteamDesktopAuthenticator
|
||||
private readonly SemaphoreSlim ConfirmationsSemaphore = new SemaphoreSlim(1);
|
||||
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(PropertyName = "shared_secret", Required = Required.Always)]
|
||||
@@ -81,9 +81,9 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private Bot Bot;
|
||||
|
||||
private MobileAuthenticator() {
|
||||
internal bool HasCorrectDeviceID => !string.IsNullOrEmpty(DeviceID) && !DeviceID.Equals("ERROR"); // "ERROR" is being used by SteamDesktopAuthenticator
|
||||
|
||||
}
|
||||
private MobileAuthenticator() { }
|
||||
|
||||
internal void Init(Bot bot) {
|
||||
if (bot == null) {
|
||||
@@ -113,19 +113,45 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint time = await GetSteamTime().ConfigureAwait(false);
|
||||
if (time == 0) {
|
||||
Logging.LogNullError(nameof(time), Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
await ConfirmationsSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
string confirmationHash = GenerateConfirmationKey(time, "conf");
|
||||
if (!string.IsNullOrEmpty(confirmationHash)) {
|
||||
return await Bot.ArchiWebHandler.HandleConfirmations(DeviceID, confirmationHash, time, confirmations, accept).ConfigureAwait(false);
|
||||
}
|
||||
try {
|
||||
uint time = await GetSteamTime().ConfigureAwait(false);
|
||||
if (time == 0) {
|
||||
Logging.LogNullError(nameof(time), Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(confirmationHash), Bot.BotName);
|
||||
return false;
|
||||
string confirmationHash = GenerateConfirmationKey(time, "conf");
|
||||
if (string.IsNullOrEmpty(confirmationHash)) {
|
||||
Logging.LogNullError(nameof(confirmationHash), Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool? result = await Bot.ArchiWebHandler.HandleConfirmations(DeviceID, confirmationHash, time, confirmations, accept).ConfigureAwait(false);
|
||||
if (!result.HasValue) { // Request timed out
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.Value) { // Request succeeded
|
||||
return true;
|
||||
}
|
||||
|
||||
// Our multi request failed, this is almost always Steam fuckup that happens randomly
|
||||
// In this case, we'll accept all pending confirmations one-by-one, synchronously (as Steam can't handle them in parallel)
|
||||
// We totally ignore actual result returned by those calls, abort only if request timed out
|
||||
|
||||
foreach (Confirmation confirmation in confirmations) {
|
||||
bool? confirmationResult = await Bot.ArchiWebHandler.HandleConfirmation(DeviceID, confirmationHash, time, confirmation.ID, confirmation.Key, accept).ConfigureAwait(false);
|
||||
if (!confirmationResult.HasValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
ConfirmationsSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<Steam.ConfirmationDetails> GetConfirmationDetails(Confirmation confirmation) {
|
||||
@@ -243,22 +269,25 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
internal async Task<uint> GetSteamTime() {
|
||||
if (SteamTimeDifference != 0) {
|
||||
return (uint) (Utilities.GetUnixTime() + SteamTimeDifference);
|
||||
private async Task<uint> GetSteamTime() {
|
||||
if (SteamTimeDifference.HasValue) {
|
||||
return (uint) (Utilities.GetUnixTime() + SteamTimeDifference.GetValueOrDefault());
|
||||
}
|
||||
|
||||
await TimeSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (SteamTimeDifference == 0) {
|
||||
uint serverTime = Bot.ArchiWebHandler.GetServerTime();
|
||||
if (serverTime != 0) {
|
||||
SteamTimeDifference = (short) (serverTime - Utilities.GetUnixTime());
|
||||
try {
|
||||
if (!SteamTimeDifference.HasValue) {
|
||||
uint serverTime = Bot.ArchiWebHandler.GetServerTime();
|
||||
if (serverTime != 0) {
|
||||
SteamTimeDifference = (short) (serverTime - Utilities.GetUnixTime());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
TimeSemaphore.Release();
|
||||
}
|
||||
|
||||
TimeSemaphore.Release();
|
||||
return (uint) (Utilities.GetUnixTime() + SteamTimeDifference);
|
||||
return (uint) (Utilities.GetUnixTime() + SteamTimeDifference.GetValueOrDefault());
|
||||
}
|
||||
|
||||
private string GenerateTokenForTime(uint time) {
|
||||
@@ -334,7 +363,9 @@ namespace ArchiSteamFarm {
|
||||
hash = hmac.ComputeHash(buffer);
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(hash, Base64FormattingOptions.None);
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
|
||||
public void Dispose() => ConfirmationsSemaphore.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,9 +311,10 @@ namespace ArchiSteamFarm {
|
||||
Directory.Delete(SharedInfo.DebugDirectory, true);
|
||||
Thread.Sleep(1000); // Dirty workaround giving Windows some time to sync
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(SharedInfo.DebugDirectory);
|
||||
|
||||
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(SharedInfo.DebugDirectory, "debug.txt")));
|
||||
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener());
|
||||
SteamKit2.DebugLog.Enabled = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ using ArchiSteamFarm;
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ArchiSteamFarm")]
|
||||
[assembly: AssemblyTitle(SharedInfo.ServiceName)]
|
||||
[assembly: AssemblyDescription(SharedInfo.ServiceDescription)]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ArchiSteamFarm")]
|
||||
[assembly: AssemblyProduct(SharedInfo.ServiceName)]
|
||||
[assembly: AssemblyCopyright(SharedInfo.Copyright)]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace ArchiSteamFarm {
|
||||
WCFHostname
|
||||
}
|
||||
|
||||
internal const string VersionNumber = "2.1.4.2";
|
||||
internal const string VersionNumber = "2.1.5.2";
|
||||
internal const string Copyright = "Copyright © ArchiSteamFarm 2015-2016";
|
||||
|
||||
internal const string GithubRepo = "JustArchi/ArchiSteamFarm";
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// Decline trade if we're losing anything but steam cards, or if it's non-dupes trade
|
||||
if (!tradeOffer.IsSteamCardsOnlyTradeForUs() || !tradeOffer.IsPotentiallyDupesTradeForUs()) {
|
||||
if (!tradeOffer.IsSteamCardsRequest() || !tradeOffer.IsFairTypesExchange()) {
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,12 @@ using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Utilities {
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
|
||||
internal static void Forget(this Task task) { }
|
||||
|
||||
|
||||
@@ -147,25 +147,6 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<string> UrlGetToContentRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
string result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && string.IsNullOrEmpty(result); i++) {
|
||||
result = await UrlGetToContent(request, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<HtmlDocument> UrlGetToHtmlDocumentRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
@@ -204,6 +185,25 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<T> UrlGetToJsonResultRetry<T>(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return default(T);
|
||||
}
|
||||
|
||||
string json = await UrlGetToContentRetry(request, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
return default(T);
|
||||
}
|
||||
|
||||
try {
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Identifier);
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<XmlDocument> UrlGetToXMLRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
@@ -242,23 +242,23 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
internal async Task<string> UrlPostToContentRetry(string request, ICollection<KeyValuePair<string, string>> data = null, string referer = null) {
|
||||
internal async Task<T> UrlPostToJsonResultRetry<T>(string request, ICollection<KeyValuePair<string, string>> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
return default(T);
|
||||
}
|
||||
|
||||
string result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && string.IsNullOrEmpty(result); i++) {
|
||||
result = await UrlPostToContent(request, data, referer).ConfigureAwait(false);
|
||||
string json = await UrlPostToContentRetry(request, data, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
return default(T);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result)) {
|
||||
return result;
|
||||
try {
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Identifier);
|
||||
return default(T);
|
||||
}
|
||||
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<byte[]> UrlGetToBytes(string request, string referer = null) {
|
||||
@@ -291,6 +291,25 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> UrlGetToContentRetry(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
string result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && string.IsNullOrEmpty(result); i++) {
|
||||
result = await UrlGetToContent(request, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<HtmlDocument> UrlGetToHtmlDocument(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
@@ -419,6 +438,25 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> UrlPostToContentRetry(string request, ICollection<KeyValuePair<string, string>> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
Logging.LogNullError(nameof(request), Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
string result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && string.IsNullOrEmpty(result); i++) {
|
||||
result = await UrlPostToContent(request, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Logging.LogGenericWarning("Request failed even after " + MaxRetries + " tries", Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UrlPostToResponse(string request, IEnumerable<KeyValuePair<string, string>> data = null, string referer = null) {
|
||||
if (!string.IsNullOrEmpty(request)) {
|
||||
return await UrlRequest(request, HttpMethod.Post, data, referer).ConfigureAwait(false);
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
# Contributing
|
||||
|
||||
Before making an issue or pull request, you should carefully read **[ASF wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki)** first.
|
||||
Before making an issue or pull request, you should carefully read **[ASF wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki)** first. At least reading **[FAQ](https://github.com/JustArchi/ArchiSteamFarm/wiki/FAQ)** is mandatory.
|
||||
|
||||
## Issues
|
||||
|
||||
GitHub **[issues](https://github.com/JustArchi/ArchiSteamFarm/issues)** page is being used for ASF TODO list, regarding both features and bugs. It has rather strict policy - GitHub is not technical support and all cases that are not suggestions or bug reports should NOT be posted there. You have **[ASF chat](https://gitter.im/JustArchi/ArchiSteamFarm)** and **[Steam group](http://steamcommunity.com/groups/ascfarm/discussions/1/)** for general discussion, questions or technical issues. Please avoid using GitHub issues, unless you indeed want to report a bug or suggest an enhancement. Even prior to doing that, please make sure that you're indeed dealing with a bug, or your suggestion makes sense, preferably by asking on chat/steam group first. Invalid issues will be closed immediately.
|
||||
GitHub **[issues](https://github.com/JustArchi/ArchiSteamFarm/issues)** page is being used for ASF TODO list, regarding both features and bugs. It has rather strict policy - GitHub is NOT technical support and all cases that are not suggestions neither bug reports should NOT be posted there. You have **[ASF chat](https://gitter.im/JustArchi/ArchiSteamFarm)** and **[Steam group](http://steamcommunity.com/groups/ascfarm/discussions/1/)** for general discussion, questions or technical issues. Please avoid using GitHub issues, unless you indeed want to report a bug or suggest an enhancement. Even prior to doing that, please make sure that you're indeed dealing with a bug, or your suggestion makes sense, preferably by asking on chat/steam group first. Invalid issues will be closed immediately and won't be answered.
|
||||
|
||||
---
|
||||
|
||||
### Bugs
|
||||
|
||||
Posting a log is **mandatory**, regardless if it contains information that is relevant or not. You're allowed to make small modifications such as changing bot names to something more generic, but you should not be doing anything else. You want us to fix the bug you've encountered, then help us instead of making it harder - we're not being paid for that, and we're not forced to fix the bug you've encountered. Include as much relevant info as possible - if bug is reproducable, when it happens, if it's a result of a command - which one, does it happen always or only sometimes, with one account or all of them - everything you consider appropriate, that would help us reproduce the bug and fix it. The more information you include, the higher the chance of bug getting fixed. And this is probably what you want, right?
|
||||
Before reporting a bug you should carefully check if the "bug" you're encountering is in fact ASF bug and not technical issue that is answered in the **[FAQ](https://github.com/JustArchi/ArchiSteamFarm/wiki/FAQ#issues)** or in other place on the wiki. Typically technical issue is intentional ASF behaviour which might not match your expectations, e.g. failing to send or accept steam trades - logic for accepting and sending steam trades is outside of the ASF, as stated in the FAQ, and there is no bug related to that because it's up to Steam to accept such request, or not. If you're not sure if you're encountering ASF bug or technical issue, please use **[ASF chat](https://gitter.im/JustArchi/ArchiSteamFarm)** or **[Steam group](http://steamcommunity.com/groups/ascfarm/discussions/1/)** and avoid GitHub issues.
|
||||
|
||||
Regarding ASF bugs - Posting a log is **mandatory**, regardless if it contains information that is relevant or not. You're allowed to make small modifications such as changing bot names to something more generic, but you should not be doing anything else. You want us to fix the bug you've encountered, then help us instead of making it harder - we're not being paid for that, and we're not forced to fix the bug you've encountered. Include as much relevant info as possible - if bug is reproducable, when it happens, if it's a result of a command - which one, does it happen always or only sometimes, with one account or all of them - everything you consider appropriate, that would help us reproduce the bug and fix it. The more information you include, the higher the chance of bug getting fixed. If nobody is able to reproduce your bug, there is also no way of blindly fixing it.
|
||||
|
||||
It would also be cool if you could reproduce your issue on latest pre-release (and not stable) version, as this is most recent codebase that might include not-yet-released fix for your issue already. Of course, that is not mandatory, as ASF offers support for both latest pre-release as well as latest stable versions.
|
||||
|
||||
---
|
||||
|
||||
@@ -28,3 +32,22 @@ In general any pull request is welcome and should be accepted, unless there is a
|
||||
|
||||
Every pull request is carefully examined by our continuous integration system - it won't be accepted if it doesn't compile properly or causes any test to fail. We also expect that you at least barely tested the modification you're trying to add, and not blindly editing the file without even checking if it compiles. Consider the fact that you're not coding it only for yourself, but for thousands of users.
|
||||
|
||||
---
|
||||
|
||||
### License
|
||||
|
||||
ASF is using **[Apache License 2.0](https://github.com/JustArchi/ArchiSteamFarm/blob/master/LICENSE-2.0.txt)**.
|
||||
|
||||
> Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions.
|
||||
|
||||
The license also permits you to:
|
||||
|
||||
> You may add Your own copyright statement to Your modifications(...)
|
||||
|
||||
Adding your own copyright statement is totally fine and it should be in format of **[copyright and contact](https://github.com/JustArchi/ArchiSteamFarm/blob/master/ArchiSteamFarm/Program.cs#L8-L9)**, specified below all currently existing statements of the file you're modifying. Adding such statement is not mandatory when sending PRs, and it's up to you to decide if you want to put one, or not.
|
||||
|
||||
---
|
||||
|
||||
### Code style
|
||||
|
||||
Please stick with ASF code style when submitting PRs. In repo you can find standard **[VS settings](https://github.com/JustArchi/ArchiSteamFarm/blob/master/CodeStyle.vssettings)** file that you can use in Visual Studio for import. In addition to that, there is also **[DotSettings](https://github.com/JustArchi/ArchiSteamFarm/blob/master/ArchiSteamFarm.sln.DotSettings)** file for **[ReSharper](https://www.jetbrains.com/resharper/)** (optional). Consistency is the key.
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -36,10 +37,14 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>cirno.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
@@ -118,7 +123,11 @@
|
||||
copy "$(TargetDir)$(TargetName).exe" "$(SolutionDir)out\ASF-ConfigGenerator.exe"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mono --llvm --server -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-ConfigGenerator.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
if [ -f "$(SolutionDir)mono_envsetup.sh" ]; then
|
||||
. "$(SolutionDir)mono_envsetup.sh"
|
||||
fi
|
||||
|
||||
mono "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-ConfigGenerator.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
rm "$(SolutionDir)out/ASF-ConfigGenerator.exe.config"
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -5,11 +5,11 @@ using ArchiSteamFarm;
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ConfigGenerator")]
|
||||
[assembly: AssemblyTitle(SharedInfo.ServiceName + "-ConfigGenerator")]
|
||||
[assembly: AssemblyDescription(SharedInfo.ServiceDescription)]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ConfigGenerator")]
|
||||
[assembly: AssemblyProduct(SharedInfo.ServiceName + "-ConfigGenerator")]
|
||||
[assembly: AssemblyCopyright(SharedInfo.Copyright)]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -24,19 +24,25 @@
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>cirno.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="HtmlAgilityPack, Version=1.4.9.5, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll</HintPath>
|
||||
@@ -63,18 +69,10 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<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" />
|
||||
@@ -205,6 +203,19 @@
|
||||
<None Include="Resources\SteamUnknownAvatar.jpg" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
copy "$(TargetDir)$(TargetName).exe" "$(SolutionDir)out\ASF-GUI.exe"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
if [ -f "$(SolutionDir)mono_envsetup.sh" ]; then
|
||||
. "$(SolutionDir)mono_envsetup.sh"
|
||||
fi
|
||||
|
||||
mono "$(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>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -172,7 +171,6 @@ namespace ArchiSteamFarm {
|
||||
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal static void LogGenericDebug(string message, string botName = SharedInfo.ASF, [CallerMemberName] string previousMethodName = null) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
|
||||
@@ -126,9 +126,10 @@ namespace ArchiSteamFarm {
|
||||
Directory.Delete(SharedInfo.DebugDirectory, true);
|
||||
Thread.Sleep(1000); // Dirty workaround giving Windows some time to sync
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(SharedInfo.DebugDirectory);
|
||||
|
||||
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(SharedInfo.DebugDirectory, "debug.txt")));
|
||||
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener());
|
||||
SteamKit2.DebugLog.Enabled = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ using ArchiSteamFarm;
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GUI")]
|
||||
[assembly: AssemblyTitle(SharedInfo.ServiceName + "-GUI")]
|
||||
[assembly: AssemblyDescription(SharedInfo.ServiceDescription)]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GUI")]
|
||||
[assembly: AssemblyProduct(SharedInfo.ServiceName + "-GUI")]
|
||||
[assembly: AssemblyCopyright(SharedInfo.Copyright)]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -8,7 +8,8 @@ ArchiSteamFarm
|
||||
[](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)
|
||||
[](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)
|
||||
|
||||
[](https://www.paypal.me/JustArchi/1usd)
|
||||
[](https://www.paypal.me/JustArchi/1usd)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4)
|
||||
[](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)
|
||||
|
||||
---
|
||||
|
||||
@@ -13,4 +13,4 @@ notifications:
|
||||
method: POST
|
||||
on_build_success: true
|
||||
on_build_failure: true
|
||||
on_build_status_changed: true
|
||||
on_build_status_changed: false
|
||||
|
||||
9
cc.sh
9
cc.sh
@@ -5,7 +5,6 @@ BUILD="Release"
|
||||
AOT=0
|
||||
CLEAN=0
|
||||
|
||||
MONO_ARGS=("--aot" "--llvm" "--server" "-O=all")
|
||||
XBUILD_ARGS=("/nologo")
|
||||
BINARIES=("ArchiSteamFarm/bin/Release/ArchiSteamFarm.exe")
|
||||
SOLUTION="ArchiSteamFarm.sln"
|
||||
@@ -29,6 +28,12 @@ XBUILD_ARGS+=("/p:Configuration=$BUILD")
|
||||
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
if [[ -f "mono_envsetup.sh" ]]; then
|
||||
set +u
|
||||
source "mono_envsetup.sh"
|
||||
set -u
|
||||
fi
|
||||
|
||||
if [[ -d ".git" ]] && hash git &>/dev/null; then
|
||||
git pull || true
|
||||
fi
|
||||
@@ -60,7 +65,7 @@ if [[ "$AOT" -eq 1 && "$BUILD" = "Release" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
mono "${MONO_ARGS[@]}" "$BINARY"
|
||||
mono --aot "$BINARY"
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
68
mono_envsetup.sh
Normal file
68
mono_envsetup.sh
Normal file
@@ -0,0 +1,68 @@
|
||||
MONO_DEBUG_IF_AVAILABLE() {
|
||||
local PREVIOUS_MONO_DEBUG="$MONO_DEBUG"
|
||||
|
||||
# Add change if needed
|
||||
if [ -z "$PREVIOUS_MONO_DEBUG" ]; then
|
||||
export MONO_DEBUG="$1"
|
||||
elif echo "$PREVIOUS_MONO_DEBUG" | grep -Fq "$1"; then
|
||||
echo "Success: $1 already exists"
|
||||
return 0
|
||||
else
|
||||
export MONO_DEBUG="${PREVIOUS_MONO_DEBUG},${1}"
|
||||
fi
|
||||
|
||||
# If we did a change, check if Mono supports that option
|
||||
# If not, it will be listed as invalid option on line 1
|
||||
if mono "" 2>&1 | head -n 1 | grep -Fq "$1"; then
|
||||
echo "Failure: $1"
|
||||
export MONO_DEBUG="$PREVIOUS_MONO_DEBUG"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Success: $1"
|
||||
return 0
|
||||
}
|
||||
|
||||
VERSION_GREATER() {
|
||||
if [ "$1" = "$2" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
! VERSION_LESS_EQUAL "$1" "$2"
|
||||
}
|
||||
|
||||
VERSION_GREATER_EQUAL() {
|
||||
! VERSION_LESS "$1" "$2"
|
||||
}
|
||||
|
||||
VERSION_LESS() {
|
||||
if [ "$1" = "$2" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
VERSION_LESS_EQUAL "$1" "$2"
|
||||
}
|
||||
|
||||
VERSION_LESS_EQUAL() {
|
||||
[ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n 1)" ]
|
||||
}
|
||||
|
||||
echo "Mono environment setup executed!"
|
||||
|
||||
MONO_VERSION="$(mono -V | head -n 1 | cut -d ' ' -f 5)"
|
||||
|
||||
echo "Mono version: $MONO_VERSION"
|
||||
|
||||
if VERSION_GREATER_EQUAL "$MONO_VERSION" "4.6.0"; then
|
||||
echo "INFO: Appending no-compact-seq-points to MONO_DEBUG..."
|
||||
MONO_DEBUG_IF_AVAILABLE "no-compact-seq-points"
|
||||
fi
|
||||
|
||||
if [ -z "$MONO_ENV_OPTIONS" ]; then
|
||||
echo "INFO: Setting MONO_ENV_OPTIONS to: --server -O=all"
|
||||
export MONO_ENV_OPTIONS="--server -O=all"
|
||||
else
|
||||
echo "INFO: Skipping setting of MONO_ENV_OPTIONS as it's already declared with value: $MONO_ENV_OPTIONS"
|
||||
fi
|
||||
|
||||
echo "Mono environment setup finished!"
|
||||
15
run.sh
15
run.sh
@@ -6,7 +6,6 @@ BUILD="Release"
|
||||
UNTIL_CLEAN_EXIT=0
|
||||
|
||||
ASF_ARGS=("")
|
||||
MONO_ARGS=("--llvm" "--server" "-O=all")
|
||||
|
||||
PRINT_USAGE() {
|
||||
echo "Usage: $0 [--until-clean-exit] [--cryptkey=] [--path=] [--server] [debug/release]"
|
||||
@@ -25,12 +24,14 @@ for ARG in "$@"; do
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$BUILD" = "Debug" ]]; then
|
||||
MONO_ARGS+=("--debug")
|
||||
fi
|
||||
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
if [[ -f "mono_envsetup.sh" ]]; then
|
||||
set +u
|
||||
source "mono_envsetup.sh"
|
||||
set -u
|
||||
fi
|
||||
|
||||
BINARY="ArchiSteamFarm/bin/$BUILD/ArchiSteamFarm.exe"
|
||||
|
||||
if [[ ! -f "$BINARY" ]]; then
|
||||
@@ -39,12 +40,12 @@ if [[ ! -f "$BINARY" ]]; then
|
||||
fi
|
||||
|
||||
if [[ "$UNTIL_CLEAN_EXIT" -eq 0 ]]; then
|
||||
mono "${MONO_ARGS[@]}" "$BINARY" "${ASF_ARGS[@]}"
|
||||
mono "$BINARY" "${ASF_ARGS[@]}"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
while [[ -f "$BINARY" ]]; do
|
||||
if mono "${MONO_ARGS[@]}" "$BINARY" "${ASF_ARGS[@]}"; then
|
||||
if mono "$BINARY" "${ASF_ARGS[@]}"; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
|
||||
Reference in New Issue
Block a user