mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-20 08:18:37 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ebf2bf891 | ||
|
|
ec2c78ea81 | ||
|
|
eb88814c72 | ||
|
|
a28ad1fdac | ||
|
|
d36eaebbfe | ||
|
|
55067c669e | ||
|
|
9d882784ee | ||
|
|
829bd8ce2f | ||
|
|
532808c65d | ||
|
|
cc317e10a8 | ||
|
|
8aaee38a85 | ||
|
|
30c69cf57c | ||
|
|
4219107d2b | ||
|
|
ea89ef6696 | ||
|
|
4b1fc7ae56 | ||
|
|
6038faaa5f | ||
|
|
e3f87e4a19 | ||
|
|
90d74de169 | ||
|
|
b781195b77 | ||
|
|
c74d4c857e | ||
|
|
f1e67ee333 | ||
|
|
8d89095670 | ||
|
|
7fe65144c1 | ||
|
|
4404874e52 | ||
|
|
a1cf8c91c2 | ||
|
|
b7eac82596 | ||
|
|
b73bdee5af | ||
|
|
51ce849aee | ||
|
|
78af201cdb | ||
|
|
a580b6ab61 | ||
|
|
3cb24e19d9 | ||
|
|
399d438f8d | ||
|
|
00fb1b7c69 | ||
|
|
f477b79fa2 | ||
|
|
8ead6be751 | ||
|
|
e7912a05e7 | ||
|
|
1bb19415d4 | ||
|
|
aa063425ce | ||
|
|
e5a39cc0de | ||
|
|
6460918bdc | ||
|
|
0d2f3f09b0 | ||
|
|
711d573e28 | ||
|
|
e90780ddac | ||
|
|
cf4e9172ee | ||
|
|
9d0cc07d4e | ||
|
|
8eeab55d0b | ||
|
|
31387fe6a1 | ||
|
|
556a56f2d3 | ||
|
|
82a992ee9c | ||
|
|
be01239114 | ||
|
|
7b74577ce0 | ||
|
|
99cc57616c | ||
|
|
0af6d48a3c | ||
|
|
bbe38e34ee | ||
|
|
e937579c46 | ||
|
|
ac69473fb3 | ||
|
|
fb1f2910dd | ||
|
|
1a917a668c | ||
|
|
065d29177a | ||
|
|
b5cc74ca42 | ||
|
|
24ad984568 | ||
|
|
d48e0b06b6 | ||
|
|
cb0d1c9484 | ||
|
|
c75d99eb86 |
@@ -15,6 +15,7 @@ matrix:
|
||||
- mono: weekly
|
||||
# - mono: alpha
|
||||
# - mono: beta
|
||||
- mono: latest
|
||||
|
||||
before_script:
|
||||
- source mono_envsetup.sh
|
||||
|
||||
@@ -34,8 +34,6 @@ using ArchiSteamFarm.JSON;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class ASF {
|
||||
internal static readonly ArchiLogger ArchiLogger = new ArchiLogger(SharedInfo.ASF);
|
||||
|
||||
private static readonly ConcurrentDictionary<Bot, DateTime> LastWriteTimes = new ConcurrentDictionary<Bot, DateTime>();
|
||||
|
||||
private static Timer AutoUpdatesTimer;
|
||||
@@ -44,7 +42,7 @@ namespace ArchiSteamFarm {
|
||||
internal static async Task CheckForUpdate(bool updateOverride = false) {
|
||||
string exeFile = Assembly.GetEntryAssembly().Location;
|
||||
if (string.IsNullOrEmpty(exeFile)) {
|
||||
ArchiLogger.LogNullError(nameof(exeFile));
|
||||
Program.ArchiLogger.LogNullError(nameof(exeFile));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,8 +56,8 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
File.Delete(oldExeFile);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
ArchiLogger.LogGenericError("Could not remove old ASF binary, please remove " + oldExeFile + " manually in order for update function to work!");
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericError("Could not remove old ASF binary, please remove " + oldExeFile + " manually in order for update function to work!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +70,7 @@ namespace ArchiSteamFarm {
|
||||
TimeSpan.FromDays(1) // Period
|
||||
);
|
||||
|
||||
ArchiLogger.LogGenericInfo("ASF will automatically check for new versions every 24 hours");
|
||||
Program.ArchiLogger.LogGenericInfo("ASF will automatically check for new versions every 24 hours");
|
||||
}
|
||||
|
||||
string releaseURL = SharedInfo.GithubReleaseURL;
|
||||
@@ -80,20 +78,20 @@ namespace ArchiSteamFarm {
|
||||
releaseURL += "/latest";
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo("Checking new version...");
|
||||
Program.ArchiLogger.LogGenericInfo("Checking new version...");
|
||||
|
||||
GitHub.ReleaseResponse releaseResponse;
|
||||
|
||||
if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
|
||||
releaseResponse = await Program.WebBrowser.UrlGetToJsonResultRetry<GitHub.ReleaseResponse>(releaseURL).ConfigureAwait(false);
|
||||
if (releaseResponse == null) {
|
||||
ArchiLogger.LogGenericWarning("Could not check latest version!");
|
||||
Program.ArchiLogger.LogGenericWarning("Could not check latest version!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
List<GitHub.ReleaseResponse> releases = await Program.WebBrowser.UrlGetToJsonResultRetry<List<GitHub.ReleaseResponse>>(releaseURL).ConfigureAwait(false);
|
||||
if ((releases == null) || (releases.Count == 0)) {
|
||||
ArchiLogger.LogGenericWarning("Could not check latest version!");
|
||||
Program.ArchiLogger.LogGenericWarning("Could not check latest version!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,33 +99,33 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(releaseResponse.Tag)) {
|
||||
ArchiLogger.LogGenericWarning("Could not check latest version!");
|
||||
Program.ArchiLogger.LogGenericWarning("Could not check latest version!");
|
||||
return;
|
||||
}
|
||||
|
||||
Version newVersion = new Version(releaseResponse.Tag);
|
||||
|
||||
ArchiLogger.LogGenericInfo("Local version: " + SharedInfo.Version + " | Remote version: " + newVersion);
|
||||
Program.ArchiLogger.LogGenericInfo("Local version: " + SharedInfo.Version + " | Remote version: " + newVersion);
|
||||
|
||||
if (SharedInfo.Version.CompareTo(newVersion) >= 0) { // If local version is the same or newer than remote version
|
||||
return;
|
||||
}
|
||||
|
||||
if (!updateOverride && !Program.GlobalConfig.AutoUpdates) {
|
||||
ArchiLogger.LogGenericInfo("New version is available!");
|
||||
ArchiLogger.LogGenericInfo("Consider updating yourself!");
|
||||
Program.ArchiLogger.LogGenericInfo("New version is available!");
|
||||
Program.ArchiLogger.LogGenericInfo("Consider updating yourself!");
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (File.Exists(oldExeFile)) {
|
||||
ArchiLogger.LogGenericWarning("Refusing to proceed with auto update as old " + oldExeFile + " binary could not be removed, please remove it manually");
|
||||
Program.ArchiLogger.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) {
|
||||
ArchiLogger.LogGenericWarning("Could not proceed with update because that version doesn't include assets!");
|
||||
Program.ArchiLogger.LogGenericWarning("Could not proceed with update because that version doesn't include assets!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,17 +133,17 @@ namespace ArchiSteamFarm {
|
||||
GitHub.ReleaseResponse.Asset binaryAsset = releaseResponse.Assets.FirstOrDefault(asset => !string.IsNullOrEmpty(asset.Name) && asset.Name.Equals(exeFileName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (binaryAsset == null) {
|
||||
ArchiLogger.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!");
|
||||
Program.ArchiLogger.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(binaryAsset.DownloadURL)) {
|
||||
ArchiLogger.LogGenericWarning("Could not proceed with update because download URL is empty!");
|
||||
Program.ArchiLogger.LogGenericWarning("Could not proceed with update because download URL is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo("Downloading new version...");
|
||||
ArchiLogger.LogGenericInfo("While waiting, consider donating if you appreciate the work being done :)");
|
||||
Program.ArchiLogger.LogGenericInfo("Downloading new version...");
|
||||
Program.ArchiLogger.LogGenericInfo("While waiting, consider donating if you appreciate the work being done :)");
|
||||
|
||||
byte[] result = await Program.WebBrowser.UrlGetToBytesRetry(binaryAsset.DownloadURL).ConfigureAwait(false);
|
||||
if (result == null) {
|
||||
@@ -158,7 +156,7 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
File.WriteAllBytes(newExeFile, result);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,7 +164,7 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
File.Move(exeFile, oldExeFile);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
try {
|
||||
// Cleanup
|
||||
File.Delete(newExeFile);
|
||||
@@ -180,7 +178,7 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
File.Move(newExeFile, exeFile);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
try {
|
||||
// Cleanup
|
||||
File.Move(oldExeFile, exeFile);
|
||||
@@ -191,26 +189,17 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo("Update process finished!");
|
||||
|
||||
if (Program.GlobalConfig.AutoRestart) {
|
||||
ArchiLogger.LogGenericInfo("Restarting...");
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
Program.Restart();
|
||||
} else {
|
||||
ArchiLogger.LogGenericInfo("Exiting...");
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
Program.Exit();
|
||||
}
|
||||
Program.ArchiLogger.LogGenericInfo("Update process finished!");
|
||||
await RestartOrExit().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal static void InitBots() {
|
||||
internal static async Task InitBots() {
|
||||
if (Bot.Bots.Count != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Before attempting to connect, initialize our list of CMs
|
||||
Bot.InitializeCMs(Program.GlobalDatabase.CellID, Program.GlobalDatabase.ServerListProvider);
|
||||
await Bot.InitializeCMs(Program.GlobalDatabase.CellID, Program.GlobalDatabase.ServerListProvider).ConfigureAwait(false);
|
||||
|
||||
foreach (string botName in Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension)) {
|
||||
switch (botName) {
|
||||
@@ -224,7 +213,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (Bot.Bots.Count == 0) {
|
||||
ArchiLogger.LogGenericWarning("No bots are defined, did you forget to configure your ASF?");
|
||||
Program.ArchiLogger.LogGenericWarning("No bots are defined, did you forget to configure your ASF?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +236,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task CreateBot(string botName) {
|
||||
if (string.IsNullOrEmpty(botName)) {
|
||||
ArchiLogger.LogNullError(nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(botName));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -267,7 +256,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async void OnChanged(object sender, FileSystemEventArgs e) {
|
||||
if ((sender == null) || (e == null)) {
|
||||
ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
Program.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -277,8 +266,8 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (botName.Equals(SharedInfo.ASF)) {
|
||||
ArchiLogger.LogGenericError("Global config file has been changed, restarting...");
|
||||
Program.Restart();
|
||||
Program.ArchiLogger.LogGenericWarning("Global config file has been changed!");
|
||||
await RestartOrExit().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -319,7 +308,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static void OnCreated(object sender, FileSystemEventArgs e) {
|
||||
if ((sender == null) || (e == null)) {
|
||||
ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
Program.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -333,7 +322,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static void OnDeleted(object sender, FileSystemEventArgs e) {
|
||||
if ((sender == null) || (e == null)) {
|
||||
ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
Program.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -343,7 +332,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (botName.Equals(SharedInfo.ASF)) {
|
||||
ArchiLogger.LogGenericError("Global config file has been removed, exiting...");
|
||||
Program.ArchiLogger.LogGenericError("Global config file has been removed, exiting...");
|
||||
Program.Exit(1);
|
||||
return;
|
||||
}
|
||||
@@ -356,7 +345,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static void OnRenamed(object sender, RenamedEventArgs e) {
|
||||
if ((sender == null) || (e == null)) {
|
||||
ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
Program.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -366,7 +355,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (oldBotName.Equals(SharedInfo.ASF)) {
|
||||
ArchiLogger.LogGenericError("Global config file has been renamed, exiting...");
|
||||
Program.ArchiLogger.LogGenericError("Global config file has been renamed, exiting...");
|
||||
Program.Exit(1);
|
||||
return;
|
||||
}
|
||||
@@ -384,6 +373,18 @@ namespace ArchiSteamFarm {
|
||||
CreateBot(newBotName).Forget();
|
||||
}
|
||||
|
||||
private static async Task RestartOrExit() {
|
||||
if (Program.GlobalConfig.AutoRestart) {
|
||||
Program.ArchiLogger.LogGenericInfo("Restarting...");
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
Program.Restart();
|
||||
} else {
|
||||
Program.ArchiLogger.LogGenericInfo("Exiting...");
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
Program.Exit();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class BotConfigEventArgs : EventArgs {
|
||||
internal readonly BotConfig BotConfig;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.CMsgs;
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
|
||||
@@ -75,6 +76,24 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal void AcceptClanInvite(ulong clanID, bool accept) {
|
||||
if (clanID == 0) {
|
||||
ArchiLogger.LogNullError(nameof(clanID));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Client.IsConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClientMsg<CMsgClientClanInviteAction> request = new ClientMsg<CMsgClientClanInviteAction>();
|
||||
|
||||
request.Body.ClanID = clanID;
|
||||
request.Body.AcceptInvite = accept;
|
||||
|
||||
Client.Send(request);
|
||||
}
|
||||
|
||||
// TODO: Remove me once https://github.com/SteamRE/SteamKit/issues/305 is fixed
|
||||
internal void LogOnWithoutMachineID(SteamUser.LogOnDetails details) {
|
||||
if (details == null) {
|
||||
@@ -150,11 +169,19 @@ namespace ArchiSteamFarm {
|
||||
ClientMsgProtobuf<CMsgClientGamesPlayed> request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
|
||||
|
||||
if (!string.IsNullOrEmpty(gameName)) {
|
||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed { game_extra_info = gameName, game_id = new GameID { AppType = GameID.GameType.Shortcut, ModID = uint.MaxValue } });
|
||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
||||
game_extra_info = gameName,
|
||||
game_id = new GameID {
|
||||
AppType = GameID.GameType.Shortcut,
|
||||
ModID = uint.MaxValue
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
foreach (uint gameID in gameIDs.Where(gameID => gameID != 0)) {
|
||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed { game_id = new GameID(gameID) });
|
||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
||||
game_id = new GameID(gameID)
|
||||
});
|
||||
}
|
||||
|
||||
Client.Send(request);
|
||||
@@ -368,7 +395,7 @@ namespace ArchiSteamFarm {
|
||||
KeyValue receiptInfo = new KeyValue();
|
||||
using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) {
|
||||
if (!receiptInfo.TryReadAsBinary(ms)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(ms));
|
||||
Program.ArchiLogger.LogNullError(nameof(ms));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -385,14 +412,14 @@ namespace ArchiSteamFarm {
|
||||
// Valid, coupons have PackageID of -1 (don't ask me why)
|
||||
packageID = lineItem["ItemAppID"].AsUnsignedInteger();
|
||||
if (packageID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(packageID));
|
||||
Program.ArchiLogger.LogNullError(nameof(packageID));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string gameName = lineItem["ItemDescription"].Value;
|
||||
if (string.IsNullOrEmpty(gameName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(gameName));
|
||||
Program.ArchiLogger.LogNullError(nameof(gameName));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,15 @@ namespace ArchiSteamFarm {
|
||||
Logger.Debug($"{previousMethodName}() {message}");
|
||||
}
|
||||
|
||||
internal void LogGenericDebugException(Exception exception, [CallerMemberName] string previousMethodName = null) {
|
||||
if (exception == null) {
|
||||
LogNullError(nameof(exception));
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Debug(exception, $"{previousMethodName}()");
|
||||
}
|
||||
|
||||
internal void LogGenericError(string message, [CallerMemberName] string previousMethodName = null) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
LogNullError(nameof(message));
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
<Compile Include="ASF.cs" />
|
||||
<Compile Include="Bot.cs" />
|
||||
<Compile Include="BotConfig.cs" />
|
||||
<Compile Include="CMsgs\CMsgClientClanInviteAction.cs" />
|
||||
<Compile Include="ConcurrentEnumerator.cs" />
|
||||
<Compile Include="ConcurrentHashSet.cs" />
|
||||
<Compile Include="CryptoHelper.cs" />
|
||||
@@ -137,6 +138,7 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SharedInfo.cs" />
|
||||
<Compile Include="Statistics.cs" />
|
||||
<Compile Include="SteamSaleEvent.cs" />
|
||||
<Compile Include="Trading.cs" />
|
||||
<Compile Include="Utilities.cs" />
|
||||
<Compile Include="WCF.cs" />
|
||||
|
||||
@@ -40,11 +40,15 @@ using Formatting = Newtonsoft.Json.Formatting;
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ArchiWebHandler : IDisposable {
|
||||
private const byte MinSessionTTL = GlobalConfig.DefaultHttpTimeout / 4; // Assume session is valid for at least that amount of seconds
|
||||
private const string SteamCommunityHost = "steamcommunity.com";
|
||||
private const string SteamStoreHost = "store.steampowered.com";
|
||||
|
||||
private static string SteamCommunityURL = "https://" + SteamCommunityHost;
|
||||
private static string SteamStoreURL = "https://" + SteamStoreHost;
|
||||
// We must use HTTPS for SteamCommunity, as http would make certain POST requests failing (trades)
|
||||
private const string SteamCommunityHost = "steamcommunity.com";
|
||||
private const string SteamCommunityURL = "https://" + SteamCommunityHost;
|
||||
|
||||
// We could (and should) use HTTPS for SteamStore, but that would make certain POST requests failing
|
||||
private const string SteamStoreHost = "store.steampowered.com";
|
||||
private const string SteamStoreURL = "http://" + SteamStoreHost;
|
||||
|
||||
private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000; // This must be int type
|
||||
|
||||
private readonly Bot Bot;
|
||||
@@ -68,20 +72,20 @@ namespace ArchiSteamFarm {
|
||||
|
||||
public void Dispose() => SessionSemaphore.Dispose();
|
||||
|
||||
internal async Task AcceptTradeOffer(ulong tradeID) {
|
||||
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
|
||||
if (tradeID == 0) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(tradeID));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(sessionID));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
string referer = SteamCommunityURL + "/tradeoffer/" + tradeID;
|
||||
@@ -93,7 +97,7 @@ namespace ArchiSteamFarm {
|
||||
{ "tradeofferid", tradeID.ToString() }
|
||||
};
|
||||
|
||||
await WebBrowser.UrlPostRetry(request, data, referer).ConfigureAwait(false);
|
||||
return await WebBrowser.UrlPostRetry(request, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task<bool> AddFreeLicense(uint subID) {
|
||||
@@ -112,7 +116,7 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
string request = SteamStoreURL + "/checkout/addfreelicense";
|
||||
const string request = SteamStoreURL + "/checkout/addfreelicense";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(3) {
|
||||
{ "sessionid", sessionID },
|
||||
{ "subid", subID.ToString() },
|
||||
@@ -123,6 +127,31 @@ namespace ArchiSteamFarm {
|
||||
return htmlDocument?.DocumentNode.SelectSingleNode("//div[@class='add_free_content_success_area']") != null;
|
||||
}
|
||||
|
||||
internal async Task<bool> ClearFromDiscoveryQueue(uint appID) {
|
||||
if (appID == 0) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appID));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamStoreURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(sessionID));
|
||||
return false;
|
||||
}
|
||||
|
||||
string request = SteamStoreURL + "/app/" + appID;
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(2) {
|
||||
{ "sessionid", sessionID },
|
||||
{ "appid_to_clear_from_queue", appID.ToString() }
|
||||
};
|
||||
|
||||
return await WebBrowser.UrlPostRetry(request, data).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal void DeclineTradeOffer(ulong tradeID) {
|
||||
if ((tradeID == 0) || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(tradeID) + " || " + nameof(Bot.BotConfig.SteamApiKey));
|
||||
@@ -135,7 +164,11 @@ namespace ArchiSteamFarm {
|
||||
iEconService.Timeout = Timeout;
|
||||
|
||||
try {
|
||||
response = iEconService.DeclineTradeOffer(tradeofferid: tradeID.ToString(), method: WebRequestMethods.Http.Post, secure: !Program.GlobalConfig.ForceHttp);
|
||||
response = iEconService.DeclineTradeOffer(
|
||||
tradeofferid: tradeID.ToString(),
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
@@ -147,6 +180,27 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<HashSet<uint>> GenerateNewDiscoveryQueue() {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamStoreURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(sessionID));
|
||||
return null;
|
||||
}
|
||||
|
||||
const string request = SteamStoreURL + "/explore/generatenewdiscoveryqueue";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(2) {
|
||||
{ "sessionid", sessionID },
|
||||
{ "queuetype", "0" }
|
||||
};
|
||||
|
||||
Steam.NewDiscoveryQueueResponse output = await WebBrowser.UrlPostToJsonResultRetry<Steam.NewDiscoveryQueueResponse>(request, data).ConfigureAwait(false);
|
||||
return output?.Queue;
|
||||
}
|
||||
|
||||
internal HashSet<Steam.TradeOffer> GetActiveTradeOffers() {
|
||||
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(Bot.BotConfig.SteamApiKey));
|
||||
@@ -159,7 +213,12 @@ namespace ArchiSteamFarm {
|
||||
iEconService.Timeout = Timeout;
|
||||
|
||||
try {
|
||||
response = iEconService.GetTradeOffers(get_received_offers: 1, active_only: 1, get_descriptions: 1, secure: !Program.GlobalConfig.ForceHttp);
|
||||
response = iEconService.GetTradeOffers(
|
||||
get_received_offers: 1,
|
||||
active_only: 1,
|
||||
get_descriptions: 1,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
@@ -301,12 +360,21 @@ namespace ArchiSteamFarm {
|
||||
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task<HtmlDocument> GetDiscoveryQueuePage() {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const string request = SteamStoreURL + "/explore?l=english";
|
||||
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task<HashSet<ulong>> GetFamilySharingSteamIDs() {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamStoreURL + "/account/managedevices";
|
||||
const string request = SteamStoreURL + "/account/managedevices";
|
||||
HtmlDocument htmlDocument = await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
||||
|
||||
HtmlNodeCollection htmlNodes = htmlDocument?.DocumentNode.SelectNodes("(//table[@class='accountTable'])[last()]//a/@data-miniprofile");
|
||||
@@ -349,7 +417,12 @@ namespace ArchiSteamFarm {
|
||||
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task<HashSet<Steam.Item>> GetMySteamInventory(bool tradable) {
|
||||
internal async Task<HashSet<Steam.Item>> GetMySteamInventory(bool tradable, HashSet<Steam.Item.EType> wantedTypes) {
|
||||
if ((wantedTypes == null) || (wantedTypes.Count == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(wantedTypes));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
@@ -445,6 +518,10 @@ namespace ArchiSteamFarm {
|
||||
steamItem.Type = description.Item2;
|
||||
}
|
||||
|
||||
if (!wantedTypes.Contains(steamItem.Type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(steamItem);
|
||||
}
|
||||
|
||||
@@ -470,7 +547,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/my/games/?xml=1";
|
||||
const string request = SteamCommunityURL + "/my/games/?xml=1";
|
||||
|
||||
XmlDocument response = await WebBrowser.UrlGetToXMLRetry(request).ConfigureAwait(false);
|
||||
|
||||
@@ -517,7 +594,11 @@ namespace ArchiSteamFarm {
|
||||
iPlayerService.Timeout = Timeout;
|
||||
|
||||
try {
|
||||
response = iPlayerService.GetOwnedGames(steamid: steamID, include_appinfo: 1, secure: !Program.GlobalConfig.ForceHttp);
|
||||
response = iPlayerService.GetOwnedGames(
|
||||
steamid: steamID,
|
||||
include_appinfo: 1,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
@@ -550,7 +631,10 @@ namespace ArchiSteamFarm {
|
||||
iTwoFactorService.Timeout = Timeout;
|
||||
|
||||
try {
|
||||
response = iTwoFactorService.QueryTime(method: WebRequestMethods.Http.Post, secure: !Program.GlobalConfig.ForceHttp);
|
||||
response = iTwoFactorService.QueryTime(
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
@@ -565,6 +649,15 @@ namespace ArchiSteamFarm {
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal async Task<HtmlDocument> GetSteamAwardsPage() {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const string request = SteamStoreURL + "/SteamAwards?l=english";
|
||||
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task<byte?> GetTradeHoldDuration(ulong tradeID) {
|
||||
if (tradeID == 0) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(tradeID));
|
||||
@@ -642,8 +735,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/mobileconf/multiajaxop";
|
||||
|
||||
const string request = SteamCommunityURL + "/mobileconf/multiajaxop";
|
||||
List<KeyValuePair<string, string>> data = new List<KeyValuePair<string, string>>(7 + confirmations.Count * 2) {
|
||||
new KeyValuePair<string, string>("op", accept ? "allow" : "cancel"),
|
||||
new KeyValuePair<string, string>("p", deviceID),
|
||||
@@ -663,11 +755,7 @@ namespace ArchiSteamFarm {
|
||||
return response?.Success;
|
||||
}
|
||||
|
||||
internal static void Init() {
|
||||
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
||||
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunityHost;
|
||||
SteamStoreURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamStoreHost;
|
||||
}
|
||||
internal static void Init() => Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
||||
|
||||
internal async Task<bool> Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalPin) {
|
||||
if ((steamID == 0) || (universe == EUniverse.Invalid) || string.IsNullOrEmpty(webAPIUserNonce) || string.IsNullOrEmpty(parentalPin)) {
|
||||
@@ -703,7 +791,13 @@ namespace ArchiSteamFarm {
|
||||
iSteamUserAuth.Timeout = Timeout;
|
||||
|
||||
try {
|
||||
authResult = iSteamUserAuth.AuthenticateUser(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, secure: !Program.GlobalConfig.ForceHttp);
|
||||
authResult = iSteamUserAuth.AuthenticateUser(
|
||||
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,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericException(e);
|
||||
return false;
|
||||
@@ -780,7 +874,7 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/my/inventory";
|
||||
const string request = SteamCommunityURL + "/my/inventory";
|
||||
return await WebBrowser.UrlHeadRetry(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -796,7 +890,7 @@ namespace ArchiSteamFarm {
|
||||
return ArchiHandler.PurchaseResponseCallback.EPurchaseResult.Timeout;
|
||||
}
|
||||
|
||||
string request = SteamStoreURL + "/account/validatewalletcode";
|
||||
const string request = SteamStoreURL + "/account/validatewalletcode";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
||||
{ "wallet_code", key }
|
||||
};
|
||||
@@ -840,8 +934,8 @@ namespace ArchiSteamFarm {
|
||||
itemID++;
|
||||
}
|
||||
|
||||
string referer = SteamCommunityURL + "/tradeoffer/new";
|
||||
string request = referer + "/send";
|
||||
const string referer = SteamCommunityURL + "/tradeoffer/new";
|
||||
const string request = referer + "/send";
|
||||
foreach (Dictionary<string, string> data in trades.Select(trade => new Dictionary<string, string>(6) {
|
||||
{ "sessionid", sessionID },
|
||||
{ "serverid", "1" },
|
||||
@@ -858,9 +952,35 @@ namespace ArchiSteamFarm {
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<bool> SteamAwardsVote(byte voteID, uint appID) {
|
||||
if ((voteID == 0) || (appID == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(voteID) + " || " + nameof(appID));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamStoreURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(sessionID));
|
||||
return false;
|
||||
}
|
||||
|
||||
const string request = SteamStoreURL + "/salevote";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(3) {
|
||||
{ "sessionid", sessionID },
|
||||
{ "voteid", voteID.ToString() },
|
||||
{ "appid", appID.ToString() }
|
||||
};
|
||||
|
||||
return await WebBrowser.UrlPostRetry(request, data).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static uint GetAppIDFromMarketHashName(string hashName) {
|
||||
if (string.IsNullOrEmpty(hashName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(hashName));
|
||||
Program.ArchiLogger.LogNullError(nameof(hashName));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -875,7 +995,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static Steam.Item.EType GetItemType(string name) {
|
||||
if (string.IsNullOrEmpty(name)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(name));
|
||||
Program.ArchiLogger.LogNullError(nameof(name));
|
||||
return Steam.Item.EType.Unknown;
|
||||
}
|
||||
|
||||
@@ -908,7 +1028,7 @@ namespace ArchiSteamFarm {
|
||||
private async Task<bool?> IsLoggedIn() {
|
||||
// It would make sense to use /my/profile here, but it dismisses notifications related to profile comments
|
||||
// So instead, we'll use some less intrusive link, such as /my/videos
|
||||
string request = SteamCommunityURL + "/my/videos";
|
||||
const string request = SteamCommunityURL + "/my/videos";
|
||||
|
||||
Uri uri = await WebBrowser.UrlHeadToUriRetry(request).ConfigureAwait(false);
|
||||
return !uri?.AbsolutePath.StartsWith("/login", StringComparison.Ordinal);
|
||||
@@ -916,32 +1036,32 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static bool ParseItems(Dictionary<ulong, Tuple<uint, Steam.Item.EType>> descriptions, List<KeyValue> input, HashSet<Steam.Item> output) {
|
||||
if ((descriptions == null) || (input == null) || (input.Count == 0) || (output == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(descriptions) + " || " + nameof(input) + " || " + nameof(output));
|
||||
Program.ArchiLogger.LogNullError(nameof(descriptions) + " || " + nameof(input) + " || " + nameof(output));
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (KeyValue item in input) {
|
||||
uint appID = item["appid"].AsUnsignedInteger();
|
||||
if (appID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(appID));
|
||||
Program.ArchiLogger.LogNullError(nameof(appID));
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong contextID = item["contextid"].AsUnsignedLong();
|
||||
if (contextID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(contextID));
|
||||
Program.ArchiLogger.LogNullError(nameof(contextID));
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong classID = item["classid"].AsUnsignedLong();
|
||||
if (classID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(classID));
|
||||
Program.ArchiLogger.LogNullError(nameof(classID));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint amount = item["amount"].AsUnsignedInteger();
|
||||
if (amount == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(amount));
|
||||
Program.ArchiLogger.LogNullError(nameof(amount));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -994,7 +1114,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Bot.ArchiLogger.LogGenericInfo("Unlocking parental account...");
|
||||
|
||||
string request = SteamCommunityURL + "/parental/ajaxunlock";
|
||||
const string request = SteamCommunityURL + "/parental/ajaxunlock";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
||||
{ "pin", parentalPin }
|
||||
};
|
||||
|
||||
@@ -82,27 +82,32 @@ namespace ArchiSteamFarm {
|
||||
private readonly SteamClient SteamClient;
|
||||
private readonly ConcurrentHashSet<ulong> SteamFamilySharingIDs = new ConcurrentHashSet<ulong>();
|
||||
private readonly SteamFriends SteamFriends;
|
||||
private readonly SteamSaleEvent SteamSaleEvent;
|
||||
private readonly SteamUser SteamUser;
|
||||
private readonly Trading Trading;
|
||||
|
||||
[JsonProperty]
|
||||
internal BotConfig BotConfig { get; private set; }
|
||||
|
||||
[JsonProperty]
|
||||
internal bool IsLimitedUser { get; private set; }
|
||||
|
||||
[JsonProperty]
|
||||
internal bool KeepRunning { get; private set; }
|
||||
|
||||
private Timer AcceptConfirmationsTimer;
|
||||
private string AuthCode, TwoFactorCode;
|
||||
private string AuthCode;
|
||||
private Timer ConnectingTimeoutTimer;
|
||||
private Timer FamilySharingInactivityTimer;
|
||||
private bool FirstTradeSent;
|
||||
private byte HeartBeatFailures;
|
||||
private EResult LastLogOnResult;
|
||||
private ulong LibraryLockedBySteamID;
|
||||
private bool LootingAllowed = true;
|
||||
private bool PlayingBlocked;
|
||||
private Timer SendItemsTimer;
|
||||
private bool SkipFirstShutdown;
|
||||
private string TwoFactorCode;
|
||||
|
||||
internal Bot(string botName) {
|
||||
if (string.IsNullOrEmpty(botName)) {
|
||||
@@ -203,13 +208,17 @@ namespace ArchiSteamFarm {
|
||||
CardsFarmer = new CardsFarmer(this);
|
||||
CardsFarmer.SetInitialState(BotConfig.Paused);
|
||||
|
||||
SteamSaleEvent = new SteamSaleEvent(this);
|
||||
Trading = new Trading(this);
|
||||
|
||||
if (Program.GlobalConfig.Statistics) {
|
||||
Statistics = new Statistics(this);
|
||||
}
|
||||
|
||||
HeartBeatTimer = new Timer(async e => await HeartBeat().ConfigureAwait(false), null, TimeSpan.FromMinutes(1) + TimeSpan.FromMinutes(0.2 * Bots.Count), // Delay
|
||||
HeartBeatTimer = new Timer(
|
||||
async e => await HeartBeat().ConfigureAwait(false),
|
||||
null,
|
||||
TimeSpan.FromMinutes(1) + TimeSpan.FromMinutes(0.2 * Bots.Count), // Delay
|
||||
TimeSpan.FromMinutes(1) // Period
|
||||
);
|
||||
|
||||
@@ -226,10 +235,12 @@ namespace ArchiSteamFarm {
|
||||
InitializationSemaphore.Dispose();
|
||||
SteamFamilySharingIDs.Dispose();
|
||||
OwnedPackageIDs.Dispose();
|
||||
SteamSaleEvent.Dispose();
|
||||
Trading.Dispose();
|
||||
|
||||
// Those are objects that might be null and the check should be in-place
|
||||
AcceptConfirmationsTimer?.Dispose();
|
||||
ConnectingTimeoutTimer?.Dispose();
|
||||
FamilySharingInactivityTimer?.Dispose();
|
||||
SendItemsTimer?.Dispose();
|
||||
}
|
||||
@@ -279,24 +290,32 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
internal static string GetAPIStatus() {
|
||||
var response = new { Bots };
|
||||
var response = new {
|
||||
Bots
|
||||
};
|
||||
|
||||
try {
|
||||
return JsonConvert.SerializeObject(response);
|
||||
} catch (JsonException e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InitializeCMs(uint cellID, IServerListProvider serverListProvider) {
|
||||
internal static async Task InitializeCMs(uint cellID, IServerListProvider serverListProvider) {
|
||||
if (serverListProvider == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(serverListProvider));
|
||||
Program.ArchiLogger.LogNullError(nameof(serverListProvider));
|
||||
return;
|
||||
}
|
||||
|
||||
CMClient.Servers.CellID = cellID;
|
||||
CMClient.Servers.ServerListProvider = serverListProvider;
|
||||
|
||||
// Normally we wouldn't need to do this, but there is a case where our list might be invalid or outdated
|
||||
// Ensure that we always ask once for list of up-to-date servers, even if we have list saved
|
||||
Program.ArchiLogger.LogGenericInfo("Initializing SteamDirectory...");
|
||||
await SteamDirectory.Initialize(cellID).ConfigureAwait(false);
|
||||
Program.ArchiLogger.LogGenericInfo("Done!");
|
||||
}
|
||||
|
||||
internal async Task LootIfNeeded() {
|
||||
@@ -358,15 +377,18 @@ namespace ArchiSteamFarm {
|
||||
TimeSpan period = TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod);
|
||||
|
||||
if (AcceptConfirmationsTimer == null) {
|
||||
AcceptConfirmationsTimer = new Timer(async e => await AcceptConfirmations(true).ConfigureAwait(false), null, delay, // Delay
|
||||
AcceptConfirmationsTimer = new Timer(
|
||||
async e => await AcceptConfirmations(true).ConfigureAwait(false),
|
||||
null,
|
||||
delay, // Delay
|
||||
period // Period
|
||||
);
|
||||
} else {
|
||||
AcceptConfirmationsTimer.Change(delay, period);
|
||||
}
|
||||
} else {
|
||||
AcceptConfirmationsTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
AcceptConfirmationsTimer?.Dispose();
|
||||
} else if (AcceptConfirmationsTimer != null) {
|
||||
AcceptConfirmationsTimer.Dispose();
|
||||
AcceptConfirmationsTimer = null;
|
||||
}
|
||||
|
||||
if ((BotConfig.SendTradePeriod > 0) && (BotConfig.SteamMasterID != 0)) {
|
||||
@@ -374,15 +396,18 @@ namespace ArchiSteamFarm {
|
||||
TimeSpan period = TimeSpan.FromHours(BotConfig.SendTradePeriod);
|
||||
|
||||
if (SendItemsTimer == null) {
|
||||
SendItemsTimer = new Timer(async e => await ResponseLoot(BotConfig.SteamMasterID).ConfigureAwait(false), null, delay, // Delay
|
||||
SendItemsTimer = new Timer(
|
||||
async e => await ResponseLoot(BotConfig.SteamMasterID).ConfigureAwait(false),
|
||||
null,
|
||||
delay, // Delay
|
||||
period // Period
|
||||
);
|
||||
} else {
|
||||
SendItemsTimer.Change(delay, period);
|
||||
}
|
||||
} else {
|
||||
SendItemsTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
SendItemsTimer?.Dispose();
|
||||
} else if (SendItemsTimer != null) {
|
||||
SendItemsTimer.Dispose();
|
||||
SendItemsTimer = null;
|
||||
}
|
||||
|
||||
await Initialize().ConfigureAwait(false);
|
||||
@@ -451,6 +476,8 @@ namespace ArchiSteamFarm {
|
||||
return ResponseHelp(steamID);
|
||||
case "!LOOT":
|
||||
return await ResponseLoot(steamID).ConfigureAwait(false);
|
||||
case "!LOOT^":
|
||||
return ResponseLootSwitch(steamID);
|
||||
case "!LOOTALL":
|
||||
return await ResponseLootAll(steamID).ConfigureAwait(false);
|
||||
case "!PASSWORD":
|
||||
@@ -500,6 +527,8 @@ namespace ArchiSteamFarm {
|
||||
return await ResponseFarm(steamID, args[1]).ConfigureAwait(false);
|
||||
case "!LOOT":
|
||||
return await ResponseLoot(steamID, args[1]).ConfigureAwait(false);
|
||||
case "!LOOT^":
|
||||
return ResponseLootSwitch(steamID, args[1]);
|
||||
case "!OWNS":
|
||||
if (args.Length > 2) {
|
||||
return await ResponseOwns(steamID, args[1], args[2]).ConfigureAwait(false);
|
||||
@@ -577,6 +606,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo("Shared library has not been launched in given time period, farming process resumed!");
|
||||
StopFamilySharingInactivityTimer();
|
||||
CardsFarmer.Resume(false);
|
||||
}
|
||||
|
||||
@@ -611,6 +641,16 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConnectingTimeoutTimer == null) {
|
||||
ConnectingTimeoutTimer = new Timer(
|
||||
async e => await Connect().ConfigureAwait(false),
|
||||
null,
|
||||
TimeSpan.FromMinutes(1), // Delay
|
||||
TimeSpan.FromMinutes(1) // Period
|
||||
);
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo("Connecting...");
|
||||
SteamClient.Connect();
|
||||
}
|
||||
}
|
||||
@@ -629,6 +669,11 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private void Disconnect() {
|
||||
lock (SteamClient) {
|
||||
if (ConnectingTimeoutTimer != null) {
|
||||
ConnectingTimeoutTimer.Dispose();
|
||||
ConnectingTimeoutTimer = null;
|
||||
}
|
||||
|
||||
SteamClient.Disconnect();
|
||||
}
|
||||
}
|
||||
@@ -771,12 +816,21 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsMasterClanID(ulong steamID) {
|
||||
if (steamID != 0) {
|
||||
return steamID == BotConfig.SteamMasterClanID;
|
||||
}
|
||||
|
||||
ArchiLogger.LogNullError(nameof(steamID));
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsOwner(ulong steamID) {
|
||||
if (steamID != 0) {
|
||||
return (steamID == Program.GlobalConfig.SteamOwnerID) || (Debugging.IsDebugBuild && (steamID == SharedInfo.ArchiSteamID));
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -785,7 +839,7 @@ namespace ArchiSteamFarm {
|
||||
return Regex.IsMatch(key, @"^[0-9A-Z]{4,7}-[0-9A-Z]{4,7}-[0-9A-Z]{4,7}(?:(?:-[0-9A-Z]{4,7})?(?:-[0-9A-Z]{4,7}))?$", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogNullError(nameof(key));
|
||||
Program.ArchiLogger.LogNullError(nameof(key));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -874,6 +928,11 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConnectingTimeoutTimer != null) {
|
||||
ConnectingTimeoutTimer.Dispose();
|
||||
ConnectingTimeoutTimer = null;
|
||||
}
|
||||
|
||||
if (callback.Result != EResult.OK) {
|
||||
ArchiLogger.LogGenericError("Unable to connect to Steam: " + callback.Result);
|
||||
return;
|
||||
@@ -943,6 +1002,11 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConnectingTimeoutTimer != null) {
|
||||
ConnectingTimeoutTimer.Dispose();
|
||||
ConnectingTimeoutTimer = null;
|
||||
}
|
||||
|
||||
HeartBeatFailures = 0;
|
||||
|
||||
EResult lastLogOnResult = LastLogOnResult;
|
||||
@@ -1057,10 +1121,18 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
foreach (SteamFriends.FriendsListCallback.Friend friend in callback.FriendList.Where(friend => friend.Relationship == EFriendRelationship.RequestRecipient)) {
|
||||
if (IsMaster(friend.SteamID)) {
|
||||
SteamFriends.AddFriend(friend.SteamID);
|
||||
} else if (BotConfig.IsBotAccount) {
|
||||
SteamFriends.RemoveFriend(friend.SteamID);
|
||||
if (friend.SteamID.AccountType == EAccountType.Clan) {
|
||||
if (IsMasterClanID(friend.SteamID)) {
|
||||
ArchiHandler.AcceptClanInvite(friend.SteamID, true);
|
||||
} else if (BotConfig.IsBotAccount) {
|
||||
ArchiHandler.AcceptClanInvite(friend.SteamID, false);
|
||||
}
|
||||
} else {
|
||||
if (IsMaster(friend.SteamID)) {
|
||||
SteamFriends.AddFriend(friend.SteamID);
|
||||
} else if (BotConfig.IsBotAccount) {
|
||||
SteamFriends.RemoveFriend(friend.SteamID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1103,7 +1175,7 @@ namespace ArchiSteamFarm {
|
||||
HashSet<uint> ownedPackageIDs = new HashSet<uint>(callback.LicenseList.Select(license => license.PackageID));
|
||||
OwnedPackageIDs.ReplaceIfNeededWith(ownedPackageIDs);
|
||||
|
||||
await Task.Delay(1000).ConfigureAwait(false); // Wait a second for eventual PlayingSessionStateCallback
|
||||
await Task.Delay(1000).ConfigureAwait(false); // Wait a second for eventual PlayingSessionStateCallback or SharedLibraryLockStatusCallback
|
||||
|
||||
if (!ArchiWebHandler.Ready) {
|
||||
for (byte i = 0; (i < Program.GlobalConfig.HttpTimeout) && !ArchiWebHandler.Ready; i++) {
|
||||
@@ -1115,6 +1187,13 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
// Normally we ResetGamesPlayed() in OnFarmingStopped() but there is no farming event if CardsFarmer module is disabled
|
||||
// Therefore, trigger extra ResetGamesPlayed(), but only in this specific case
|
||||
if (CardsFarmer.Paused) {
|
||||
ResetGamesPlayed();
|
||||
}
|
||||
|
||||
// We trigger OnNewGameAdded() anyway, as CardsFarmer has other things to handle regardless of being Paused or not
|
||||
await CardsFarmer.OnNewGameAdded().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -1182,7 +1261,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// Sometimes Steam won't send us our own PersonaStateCallback, so request it explicitly
|
||||
SteamFriends.RequestFriendInfo(callback.ClientSteamID, EClientPersonaStateFlag.Presence);
|
||||
SteamFriends.RequestFriendInfo(callback.ClientSteamID, EClientPersonaStateFlag.PlayerName | EClientPersonaStateFlag.Presence);
|
||||
|
||||
if (BotDatabase.MobileAuthenticator == null) {
|
||||
// Support and convert SDA files
|
||||
@@ -1279,7 +1358,17 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// Inform the steam servers that we're accepting this sentry file
|
||||
SteamUser.SendMachineAuthResponse(new SteamUser.MachineAuthDetails { JobID = callback.JobID, FileName = callback.FileName, BytesWritten = callback.BytesToWrite, FileSize = fileSize, Offset = callback.Offset, Result = EResult.OK, LastError = 0, OneTimePassword = callback.OneTimePassword, SentryFileHash = sentryHash });
|
||||
SteamUser.SendMachineAuthResponse(new SteamUser.MachineAuthDetails {
|
||||
JobID = callback.JobID,
|
||||
FileName = callback.FileName,
|
||||
BytesWritten = callback.BytesToWrite,
|
||||
FileSize = fileSize,
|
||||
Offset = callback.Offset,
|
||||
Result = EResult.OK,
|
||||
LastError = 0,
|
||||
OneTimePassword = callback.OneTimePassword,
|
||||
SentryFileHash = sentryHash
|
||||
});
|
||||
}
|
||||
|
||||
private void OnNotifications(ArchiHandler.NotificationsCallback callback) {
|
||||
@@ -1390,7 +1479,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private void ResetGamesPlayed() {
|
||||
if (PlayingBlocked) {
|
||||
if (!IsPlayingPossible || (FamilySharingInactivityTimer != null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1421,7 +1510,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> Response2FA(ulong steamID, string botName) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1460,7 +1549,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> Response2FAConfirm(ulong steamID, string botName, bool confirm) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1512,7 +1601,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> ResponseAddLicense(ulong steamID, string botName, string games) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName) || string.IsNullOrEmpty(games)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName) + " || " + nameof(games));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName) + " || " + nameof(games));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1549,13 +1638,13 @@ namespace ArchiSteamFarm {
|
||||
return !IsOwner(steamID) ? null : GetAPIStatus();
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string ResponseExit(ulong steamID) {
|
||||
if (steamID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1593,7 +1682,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> ResponseFarm(ulong steamID, string botName) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1632,6 +1721,10 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!LootingAllowed) {
|
||||
return "Looting is temporarily disabled!";
|
||||
}
|
||||
|
||||
if (!IsConnectedAndLoggedOn) {
|
||||
return "This bot instance is not connected!";
|
||||
}
|
||||
@@ -1646,30 +1739,23 @@ namespace ArchiSteamFarm {
|
||||
|
||||
await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false);
|
||||
|
||||
HashSet<Steam.Item> inventory = await ArchiWebHandler.GetMySteamInventory(true).ConfigureAwait(false);
|
||||
HashSet<Steam.Item> inventory = await ArchiWebHandler.GetMySteamInventory(true, BotConfig.LootableTypes).ConfigureAwait(false);
|
||||
if ((inventory == null) || (inventory.Count == 0)) {
|
||||
return "Nothing to send, inventory seems empty!";
|
||||
}
|
||||
|
||||
// Remove from our pending inventory all items that are not steam cards and boosters
|
||||
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)) {
|
||||
return "Trade offer failed due to error!";
|
||||
}
|
||||
|
||||
await Task.Delay(1000).ConfigureAwait(false); // Sometimes we can be too fast for Steam servers to generate confirmations, wait a short moment
|
||||
await Task.Delay(3000).ConfigureAwait(false); // Sometimes we can be too fast for Steam servers to generate confirmations, wait a short moment
|
||||
await AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, BotConfig.SteamMasterID).ConfigureAwait(false);
|
||||
return "Trade offer sent successfully!";
|
||||
}
|
||||
|
||||
private static async Task<string> ResponseLoot(ulong steamID, string botName) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1687,7 +1773,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> ResponseLootAll(ulong steamID) {
|
||||
if (steamID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1699,6 +1785,38 @@ namespace ArchiSteamFarm {
|
||||
return "Done!";
|
||||
}
|
||||
|
||||
private string ResponseLootSwitch(ulong steamID) {
|
||||
if (steamID == 0) {
|
||||
ArchiLogger.LogNullError(nameof(steamID));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!IsMaster(steamID)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LootingAllowed = !LootingAllowed;
|
||||
return "Looting is now " + (LootingAllowed ? "enabled" : "disabled") + "!";
|
||||
}
|
||||
|
||||
private static string ResponseLootSwitch(ulong steamID, string botName) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
Bot bot;
|
||||
if (Bots.TryGetValue(botName, out bot)) {
|
||||
return bot.ResponseLootSwitch(steamID);
|
||||
}
|
||||
|
||||
if (IsOwner(steamID)) {
|
||||
return "Couldn't find any bot named " + botName + "!";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<string> ResponseOwns(ulong steamID, string query) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(query)) {
|
||||
ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(query));
|
||||
@@ -1756,7 +1874,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> ResponseOwns(ulong steamID, string botName, string query) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName) || string.IsNullOrEmpty(query)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName) + " || " + nameof(query));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName) + " || " + nameof(query));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1774,7 +1892,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> ResponseOwnsAll(ulong steamID, string query) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(query)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(query));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(query));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1811,7 +1929,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string ResponsePassword(ulong steamID, string botName) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1857,7 +1975,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> ResponsePause(ulong steamID, string botName, bool sticky) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1897,7 +2015,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> ResponsePlay(ulong steamID, string botName, string games) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName) || string.IsNullOrEmpty(games)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName) + " || " + nameof(games));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName) + " || " + nameof(games));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1944,8 +2062,8 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
bool forward = !redeemFlags.HasFlag(ERedeemFlags.SkipForwarding) && (redeemFlags.HasFlag(ERedeemFlags.ForceForwarding) || BotConfig.ForwardKeysToOtherBots);
|
||||
bool distribute = !redeemFlags.HasFlag(ERedeemFlags.SkipDistribution) && (redeemFlags.HasFlag(ERedeemFlags.ForceDistribution) || BotConfig.DistributeKeys);
|
||||
bool forward = !redeemFlags.HasFlag(ERedeemFlags.SkipForwarding) && (redeemFlags.HasFlag(ERedeemFlags.ForceForwarding) || BotConfig.RedeemingPreferences.HasFlag(BotConfig.ERedeemingPreferences.Forwarding));
|
||||
bool distribute = !redeemFlags.HasFlag(ERedeemFlags.SkipDistribution) && (redeemFlags.HasFlag(ERedeemFlags.ForceDistribution) || BotConfig.RedeemingPreferences.HasFlag(BotConfig.ERedeemingPreferences.Distributing));
|
||||
message = message.Replace(",", Environment.NewLine);
|
||||
|
||||
StringBuilder response = new StringBuilder();
|
||||
@@ -2060,7 +2178,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> ResponseRedeem(ulong steamID, string botName, string message, ERedeemFlags redeemFlags = ERedeemFlags.None) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName) || string.IsNullOrEmpty(message)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName) + " || " + nameof(message));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName) + " || " + nameof(message));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2078,7 +2196,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string ResponseRejoinChat(ulong steamID) {
|
||||
if (steamID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2095,7 +2213,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string ResponseRestart(ulong steamID) {
|
||||
if (steamID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2137,7 +2255,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string ResponseResume(ulong steamID, string botName) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2174,7 +2292,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string ResponseStart(ulong steamID, string botName) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2192,7 +2310,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string ResponseStartAll(ulong steamID) {
|
||||
if (steamID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2233,6 +2351,10 @@ namespace ArchiSteamFarm {
|
||||
return "Bot " + BotName + " is paused or running in manual mode.";
|
||||
}
|
||||
|
||||
if (IsLimitedUser) {
|
||||
return "Bot " + BotName + " is limited.";
|
||||
}
|
||||
|
||||
if (CardsFarmer.CurrentGamesFarming.Count == 0) {
|
||||
return "Bot " + BotName + " is not farming anything.";
|
||||
}
|
||||
@@ -2252,7 +2374,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string ResponseStatus(ulong steamID, string botName) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2270,7 +2392,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string ResponseStatusAll(ulong steamID) {
|
||||
if (steamID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2304,7 +2426,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string ResponseStop(ulong steamID, string botName) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botName));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2331,7 +2453,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static async Task<string> ResponseUpdate(ulong steamID) {
|
||||
if (steamID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
Program.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2416,7 +2538,10 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
FamilySharingInactivityTimer = new Timer(e => CheckFamilySharingInactivity(), null, TimeSpan.FromMinutes(FamilySharingInactivityMinutes), // Delay
|
||||
FamilySharingInactivityTimer = new Timer(
|
||||
e => CheckFamilySharingInactivity(),
|
||||
null,
|
||||
TimeSpan.FromMinutes(FamilySharingInactivityMinutes), // Delay
|
||||
Timeout.InfiniteTimeSpan // Period
|
||||
);
|
||||
}
|
||||
@@ -2426,7 +2551,6 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
FamilySharingInactivityTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
FamilySharingInactivityTimer.Dispose();
|
||||
FamilySharingInactivityTimer = null;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ArchiSteamFarm.JSON;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
@@ -53,9 +54,6 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool DismissInventoryNotifications = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool DistributeKeys = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool Enabled = false;
|
||||
|
||||
@@ -65,9 +63,6 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool FarmOffline = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool ForwardKeysToOtherBots = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly HashSet<uint> GamesPlayedWhileIdle = new HashSet<uint>();
|
||||
|
||||
@@ -77,12 +72,18 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool IsBotAccount = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly HashSet<Steam.Item.EType> LootableTypes = new HashSet<Steam.Item.EType> { Steam.Item.EType.BoosterPack, Steam.Item.EType.FoilTradingCard, Steam.Item.EType.TradingCard };
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool Paused = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly ERedeemingPreferences RedeemingPreferences = ERedeemingPreferences.None;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool SendOnFarmingFinished = false;
|
||||
|
||||
@@ -121,7 +122,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal static BotConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(filePath));
|
||||
Program.ArchiLogger.LogNullError(nameof(filePath));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -134,12 +135,12 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (botConfig == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botConfig));
|
||||
Program.ArchiLogger.LogNullError(nameof(botConfig));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -155,7 +156,7 @@ namespace ArchiSteamFarm {
|
||||
return botConfig;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericWarning("Playing more than " + CardsFarmer.MaxGamesPlayedConcurrently + " games concurrently is not possible, only first " + CardsFarmer.MaxGamesPlayedConcurrently + " entries from GamesPlayedWhileIdle will be used");
|
||||
Program.ArchiLogger.LogGenericWarning("Playing more than " + CardsFarmer.MaxGamesPlayedConcurrently + " games concurrently is not possible, only first " + CardsFarmer.MaxGamesPlayedConcurrently + " entries from GamesPlayedWhileIdle will be used");
|
||||
|
||||
HashSet<uint> validGames = new HashSet<uint>(botConfig.GamesPlayedWhileIdle.Take(CardsFarmer.MaxGamesPlayedConcurrently));
|
||||
botConfig.GamesPlayedWhileIdle.IntersectWith(validGames);
|
||||
@@ -176,6 +177,14 @@ namespace ArchiSteamFarm {
|
||||
NamesDescending
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum ERedeemingPreferences : byte {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
None = 0,
|
||||
Forwarding = 1,
|
||||
Distributing = 2
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum ETradingPreferences : byte {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal static BotDatabase Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(filePath));
|
||||
Program.ArchiLogger.LogNullError(nameof(filePath));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -95,12 +95,12 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
botDatabase = JsonConvert.DeserializeObject<BotDatabase>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (botDatabase == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botDatabase));
|
||||
Program.ArchiLogger.LogNullError(nameof(botDatabase));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace ArchiSteamFarm {
|
||||
internal void Save() {
|
||||
string json = JsonConvert.SerializeObject(this);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(json));
|
||||
Program.ArchiLogger.LogNullError(nameof(json));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace ArchiSteamFarm {
|
||||
File.WriteAllText(FilePath, json);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
|
||||
Thread.Sleep(1000);
|
||||
|
||||
58
ArchiSteamFarm/CMsgs/CMsgClientClanInviteAction.cs
Normal file
58
ArchiSteamFarm/CMsgs/CMsgClientClanInviteAction.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System.IO;
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
|
||||
namespace ArchiSteamFarm.CMsgs {
|
||||
internal sealed class CMsgClientClanInviteAction : ISteamSerializableMessage {
|
||||
internal bool AcceptInvite { private get; set; }
|
||||
internal ulong ClanID { private get; set; }
|
||||
|
||||
void ISteamSerializable.Deserialize(Stream stream) {
|
||||
if (stream == null) {
|
||||
Program.ArchiLogger.LogNullError(nameof(stream));
|
||||
return;
|
||||
}
|
||||
|
||||
BinaryReader binaryReader = new BinaryReader(stream);
|
||||
ClanID = binaryReader.ReadUInt64();
|
||||
AcceptInvite = binaryReader.ReadBoolean();
|
||||
}
|
||||
|
||||
EMsg ISteamSerializableMessage.GetEMsg() => EMsg.ClientAcknowledgeClanInvite;
|
||||
|
||||
void ISteamSerializable.Serialize(Stream stream) {
|
||||
if (stream == null) {
|
||||
Program.ArchiLogger.LogNullError(nameof(stream));
|
||||
return;
|
||||
}
|
||||
|
||||
BinaryWriter binaryWriter = new BinaryWriter(stream);
|
||||
binaryWriter.Write(ClanID);
|
||||
binaryWriter.Write(AcceptInvite);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,8 @@ namespace ArchiSteamFarm {
|
||||
internal sealed class CardsFarmer : IDisposable {
|
||||
internal const byte MaxGamesPlayedConcurrently = 32; // This is limit introduced by Steam Network
|
||||
|
||||
private static readonly HashSet<uint> UntrustedAppIDs = new HashSet<uint> { 440, 570, 730 };
|
||||
|
||||
[JsonProperty]
|
||||
internal readonly ConcurrentHashSet<Game> CurrentGamesFarming = new ConcurrentHashSet<Game>();
|
||||
|
||||
@@ -62,7 +64,10 @@ namespace ArchiSteamFarm {
|
||||
Bot = bot;
|
||||
|
||||
if (Program.GlobalConfig.IdleFarmingPeriod > 0) {
|
||||
IdleFarmingTimer = new Timer(e => CheckGamesForFarming(), null, TimeSpan.FromHours(Program.GlobalConfig.IdleFarmingPeriod) + TimeSpan.FromMinutes(0.5 * Bot.Bots.Count), // Delay
|
||||
IdleFarmingTimer = new Timer(
|
||||
e => CheckGamesForFarming(),
|
||||
null,
|
||||
TimeSpan.FromHours(Program.GlobalConfig.IdleFarmingPeriod) + TimeSpan.FromMinutes(0.5 * Bot.Bots.Count), // Delay
|
||||
TimeSpan.FromHours(Program.GlobalConfig.IdleFarmingPeriod) // Period
|
||||
);
|
||||
}
|
||||
@@ -82,15 +87,16 @@ namespace ArchiSteamFarm {
|
||||
internal void OnDisconnected() => StopFarming().Forget();
|
||||
|
||||
internal async Task OnNewGameAdded() {
|
||||
// If we're not farming yet, obviously it's worth it to make a check
|
||||
if (!NowFarming) {
|
||||
// If we're not farming yet, obviously it's worth it to make a check
|
||||
StartFarming().Forget();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have Complex algorithm and some games to boost, it's also worth to make a re-check, but only in this case
|
||||
// That's because we would check for new games after our current round anyway, and having extra games in the queue right away doesn't change anything
|
||||
// Therefore, there is no need for extra restart of CardsFarmer if we have no games under 2 hours in current round
|
||||
if (Bot.BotConfig.CardDropsRestricted && (GamesToFarm.Count > 0) && (GamesToFarm.Min(game => game.HoursPlayed) < 2)) {
|
||||
// If we have Complex algorithm and some games to boost, it's also worth to make a check
|
||||
// That's because we would check for new games after our current round anyway
|
||||
await StopFarming().ConfigureAwait(false);
|
||||
StartFarming().Forget();
|
||||
}
|
||||
@@ -251,6 +257,25 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckGame(uint appID, string name, float hours) {
|
||||
if ((appID == 0) || string.IsNullOrEmpty(name) || (hours < 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(name) + " || " + nameof(hours));
|
||||
return;
|
||||
}
|
||||
|
||||
ushort? cardsRemaining = await GetCardsRemaining(appID).ConfigureAwait(false);
|
||||
if (!cardsRemaining.HasValue) {
|
||||
Bot.ArchiLogger.LogGenericWarning("Could not check cards status for " + appID + " (" + name + "), will try again later!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cardsRemaining.Value == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining.Value));
|
||||
}
|
||||
|
||||
private void CheckGamesForFarming() {
|
||||
if (NowFarming || Paused || !Bot.IsConnectedAndLoggedOn) {
|
||||
return;
|
||||
@@ -259,118 +284,167 @@ namespace ArchiSteamFarm {
|
||||
StartFarming().Forget();
|
||||
}
|
||||
|
||||
private void CheckPage(HtmlDocument htmlDocument) {
|
||||
private async Task CheckPage(HtmlDocument htmlDocument) {
|
||||
if (htmlDocument == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(htmlDocument));
|
||||
return;
|
||||
}
|
||||
|
||||
HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_title_stats']");
|
||||
if (htmlNodes == null) { // No eligible badges
|
||||
HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_title_stats_content']");
|
||||
if (htmlNodes == null) {
|
||||
// No eligible badges whatsoever
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<Task> extraTasks = new HashSet<Task>();
|
||||
|
||||
foreach (HtmlNode htmlNode in htmlNodes) {
|
||||
HtmlNode farmingNode = htmlNode.SelectSingleNode(".//a[@class='btn_green_white_innerfade btn_small_thin']");
|
||||
if (farmingNode == null) {
|
||||
continue; // This game is not needed for farming
|
||||
HtmlNode appIDNode = htmlNode.SelectSingleNode(".//div[@class='card_drop_info_dialog']");
|
||||
if (appIDNode == null) {
|
||||
// It's just a badge, nothing more
|
||||
continue;
|
||||
}
|
||||
|
||||
HtmlNode progressNode = htmlNode.SelectSingleNode(".//span[@class='progress_info_bold']");
|
||||
if (progressNode == null) {
|
||||
continue; // e.g. Holiday Sale 2015
|
||||
string appIDString = appIDNode.GetAttributeValue("id", null);
|
||||
if (string.IsNullOrEmpty(appIDString)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appIDString));
|
||||
continue;
|
||||
}
|
||||
|
||||
// AppIDs
|
||||
string steamLink = farmingNode.GetAttributeValue("href", null);
|
||||
if (string.IsNullOrEmpty(steamLink)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(steamLink));
|
||||
return;
|
||||
string[] appIDSplitted = appIDString.Split('_');
|
||||
if (appIDSplitted.Length < 5) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appIDSplitted));
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = steamLink.LastIndexOf('/');
|
||||
if (index < 0) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(index));
|
||||
return;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (steamLink.Length <= index) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(steamLink.Length));
|
||||
return;
|
||||
}
|
||||
|
||||
steamLink = steamLink.Substring(index);
|
||||
appIDString = appIDSplitted[4];
|
||||
|
||||
uint appID;
|
||||
if (!uint.TryParse(steamLink, out appID) || (appID == 0)) {
|
||||
if (!uint.TryParse(appIDString, out appID) || (appID == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appID));
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GlobalConfig.GlobalBlacklist.Contains(appID) || Program.GlobalConfig.Blacklist.Contains(appID)) {
|
||||
// We have this appID blacklisted, so skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cards
|
||||
HtmlNode progressNode = htmlNode.SelectSingleNode(".//span[@class='progress_info_bold']");
|
||||
if (progressNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(progressNode));
|
||||
continue;
|
||||
}
|
||||
|
||||
string progressText = progressNode.InnerText;
|
||||
if (string.IsNullOrEmpty(progressText)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(progressText));
|
||||
continue;
|
||||
}
|
||||
|
||||
ushort cardsRemaining = 0;
|
||||
Match progressMatch = Regex.Match(progressText, @"\d+");
|
||||
|
||||
// This might fail if we have no card drops remaining, that's fine
|
||||
if (progressMatch.Success) {
|
||||
if (!ushort.TryParse(progressMatch.Value, out cardsRemaining)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(cardsRemaining));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (cardsRemaining == 0) {
|
||||
// Normally we'd trust this information and simply skip the rest
|
||||
// However, Steam is so fucked up that we can't simply assume that it's correct
|
||||
// It's entirely possible that actual game page has different info, and badge page lied to us
|
||||
// We can't check every single game though, as this will literally kill people with cards from games they don't own
|
||||
if (!UntrustedAppIDs.Contains(appID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// To save us on extra work, check cards earned so far first
|
||||
HtmlNode cardsEarnedNode = htmlNode.SelectSingleNode(".//div[@class='card_drop_info_header']");
|
||||
string cardsEarnedText = cardsEarnedNode.InnerText;
|
||||
if (string.IsNullOrEmpty(cardsEarnedText)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(cardsEarnedText));
|
||||
continue;
|
||||
}
|
||||
|
||||
Match cardsEarnedMatch = Regex.Match(cardsEarnedText, @"\d+");
|
||||
if (!cardsEarnedMatch.Success) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(cardsEarnedMatch));
|
||||
continue;
|
||||
}
|
||||
|
||||
ushort cardsEarned;
|
||||
if (!ushort.TryParse(cardsEarnedMatch.Value, out cardsEarned)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(cardsEarned));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cardsEarned > 0) {
|
||||
// If we already earned some cards for this game, it's very likely that it's done
|
||||
// Let's hope that trusting cardsRemaining AND cardsEarned is enough
|
||||
// If I ever hear that it's not, I'll most likely need a doctor
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have no cardsRemaining and no cardsEarned, it's either:
|
||||
// - A game we don't own physically, but we have cards from it in inventory
|
||||
// - F2P game that we didn't spend any money in, but we have cards from it in inventory
|
||||
// - Steam fuckup
|
||||
// As you can guess, we must follow the rest of the logic in case of Steam fuckup
|
||||
// Please kill me ;_;
|
||||
}
|
||||
|
||||
// Hours
|
||||
HtmlNode timeNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']");
|
||||
if (timeNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(timeNode));
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
string hoursString = timeNode.InnerText;
|
||||
if (string.IsNullOrEmpty(hoursString)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(hoursString));
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
float hours = 0;
|
||||
|
||||
Match hoursMatch = Regex.Match(hoursString, @"[0-9\.,]+");
|
||||
if (hoursMatch.Success) { // Might fail if we have 0.0 hours played
|
||||
|
||||
// This might fail if we have exactly 0.0 hours played, that's fine
|
||||
if (hoursMatch.Success) {
|
||||
if (!float.TryParse(hoursMatch.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out hours)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(hours));
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Cards
|
||||
string progress = progressNode.InnerText;
|
||||
if (string.IsNullOrEmpty(progress)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(progress));
|
||||
return;
|
||||
}
|
||||
|
||||
Match progressMatch = Regex.Match(progress, @"\d+");
|
||||
if (!progressMatch.Success) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(progressMatch));
|
||||
return;
|
||||
}
|
||||
|
||||
ushort cardsRemaining;
|
||||
if (!ushort.TryParse(progressMatch.Value, out cardsRemaining) || (cardsRemaining == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(cardsRemaining));
|
||||
return;
|
||||
}
|
||||
|
||||
// Names
|
||||
HtmlNode nameNode = htmlNode.SelectSingleNode("(.//div[@class='card_drop_info_body'])[last()]");
|
||||
if (nameNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(nameNode));
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
string name = nameNode.InnerText;
|
||||
if (string.IsNullOrEmpty(name)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(name));
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We handle two cases here - normal one, and no card drops remaining
|
||||
int nameStartIndex = name.IndexOf(" by playing ", StringComparison.Ordinal);
|
||||
if (nameStartIndex <= 0) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(nameStartIndex));
|
||||
return;
|
||||
nameStartIndex = name.IndexOf("You don't have any more drops remaining for ", StringComparison.Ordinal);
|
||||
if (nameStartIndex <= 0) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(nameStartIndex));
|
||||
continue;
|
||||
}
|
||||
|
||||
nameStartIndex += 32; // + 12 below
|
||||
}
|
||||
|
||||
nameStartIndex += 12;
|
||||
@@ -378,14 +452,24 @@ namespace ArchiSteamFarm {
|
||||
int nameEndIndex = name.LastIndexOf('.');
|
||||
if (nameEndIndex <= nameStartIndex) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(nameEndIndex));
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
name = name.Substring(nameStartIndex, nameEndIndex - nameStartIndex);
|
||||
|
||||
// Final result
|
||||
GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining));
|
||||
// We have two possible cases here
|
||||
// Either we have decent info about appID, name, hours and cardsRemaining (cardsRemaining > 0)
|
||||
// OR we strongly believe that Steam lied to us, in this case we will need to check game invidually (cardsRemaining == 0)
|
||||
|
||||
if (cardsRemaining > 0) {
|
||||
GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining));
|
||||
} else {
|
||||
extraTasks.Add(CheckGame(appID, name, hours));
|
||||
}
|
||||
}
|
||||
|
||||
// If we have any pending tasks, wait for them
|
||||
await Task.WhenAll(extraTasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task CheckPage(byte page) {
|
||||
@@ -399,7 +483,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
CheckPage(htmlDocument);
|
||||
await CheckPage(htmlDocument).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<bool> Farm(Game game) {
|
||||
@@ -522,6 +606,39 @@ namespace ArchiSteamFarm {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<ushort?> GetCardsRemaining(uint appID) {
|
||||
if (appID == 0) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appID));
|
||||
return 0;
|
||||
}
|
||||
|
||||
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false);
|
||||
|
||||
HtmlNode progressNode = htmlDocument?.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||
if (progressNode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string progress = progressNode.InnerText;
|
||||
if (string.IsNullOrEmpty(progress)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(progress));
|
||||
return null;
|
||||
}
|
||||
|
||||
Match match = Regex.Match(progress, @"\d+");
|
||||
if (!match.Success) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ushort cardsRemaining;
|
||||
if (ushort.TryParse(match.Value, out cardsRemaining) && (cardsRemaining != 0)) {
|
||||
return cardsRemaining;
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogNullError(nameof(cardsRemaining));
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<bool> IsAnythingToFarm() {
|
||||
Bot.ArchiLogger.LogGenericInfo("Checking badges...");
|
||||
|
||||
@@ -550,19 +667,16 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
GamesToFarm.ClearAndTrim();
|
||||
CheckPage(htmlDocument);
|
||||
|
||||
if (maxPages == 1) {
|
||||
SortGamesToFarm();
|
||||
return GamesToFarm.Count > 0;
|
||||
}
|
||||
List<Task> tasks = new List<Task>(maxPages - 1) { CheckPage(htmlDocument) };
|
||||
|
||||
Bot.ArchiLogger.LogGenericInfo("Checking other pages...");
|
||||
if (maxPages > 1) {
|
||||
Bot.ArchiLogger.LogGenericInfo("Checking other pages...");
|
||||
|
||||
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));
|
||||
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);
|
||||
@@ -576,37 +690,16 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(game.AppID).ConfigureAwait(false);
|
||||
if (htmlDocument == null) {
|
||||
ushort? cardsRemaining = await GetCardsRemaining(game.AppID).ConfigureAwait(false);
|
||||
if (!cardsRemaining.HasValue) {
|
||||
Bot.ArchiLogger.LogGenericWarning("Could not check cards status for " + game.AppID + " (" + game.GameName + "), will try again later!");
|
||||
return null;
|
||||
}
|
||||
|
||||
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||
if (htmlNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(htmlNode));
|
||||
return null;
|
||||
}
|
||||
game.CardsRemaining = cardsRemaining.Value;
|
||||
|
||||
string progress = htmlNode.InnerText;
|
||||
if (string.IsNullOrEmpty(progress)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(progress));
|
||||
return null;
|
||||
}
|
||||
|
||||
ushort cardsRemaining = 0;
|
||||
|
||||
Match match = Regex.Match(progress, @"\d+");
|
||||
if (match.Success) {
|
||||
if (!ushort.TryParse(match.Value, out cardsRemaining)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(cardsRemaining));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
game.CardsRemaining = cardsRemaining;
|
||||
|
||||
Bot.ArchiLogger.LogGenericInfo("Status for " + game.AppID + " (" + game.GameName + "): " + cardsRemaining + " cards remaining");
|
||||
return cardsRemaining > 0;
|
||||
Bot.ArchiLogger.LogGenericInfo("Status for " + game.AppID + " (" + game.GameName + "): " + game.CardsRemaining + " cards remaining");
|
||||
return game.CardsRemaining > 0;
|
||||
}
|
||||
|
||||
private void SortGamesToFarm() {
|
||||
@@ -673,11 +766,11 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (ReferenceEquals(null, obj)) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, obj)) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal static string Decrypt(ECryptoMethod cryptoMethod, string encrypted) {
|
||||
if (string.IsNullOrEmpty(encrypted)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(encrypted));
|
||||
Program.ArchiLogger.LogNullError(nameof(encrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal static string Encrypt(ECryptoMethod cryptoMethod, string decrypted) {
|
||||
if (string.IsNullOrEmpty(decrypted)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(decrypted));
|
||||
Program.ArchiLogger.LogNullError(nameof(decrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal static void SetEncryptionKey(string key) {
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(key));
|
||||
Program.ArchiLogger.LogNullError(nameof(key));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static string DecryptAES(string encrypted) {
|
||||
if (string.IsNullOrEmpty(encrypted)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(encrypted));
|
||||
Program.ArchiLogger.LogNullError(nameof(encrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -91,14 +91,14 @@ namespace ArchiSteamFarm {
|
||||
decryptedData = SteamKit2.CryptoHelper.SymmetricDecrypt(decryptedData, key);
|
||||
return Encoding.UTF8.GetString(decryptedData);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string DecryptProtectedDataForCurrentUser(string encrypted) {
|
||||
if (string.IsNullOrEmpty(encrypted)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(encrypted));
|
||||
Program.ArchiLogger.LogNullError(nameof(encrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -108,14 +108,14 @@ namespace ArchiSteamFarm {
|
||||
|
||||
return Encoding.UTF8.GetString(decryptedData);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string EncryptAES(string decrypted) {
|
||||
if (string.IsNullOrEmpty(decrypted)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(decrypted));
|
||||
Program.ArchiLogger.LogNullError(nameof(decrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -129,14 +129,14 @@ namespace ArchiSteamFarm {
|
||||
encryptedData = SteamKit2.CryptoHelper.SymmetricEncrypt(encryptedData, key);
|
||||
return Convert.ToBase64String(encryptedData);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string EncryptProtectedDataForCurrentUser(string decrypted) {
|
||||
if (string.IsNullOrEmpty(decrypted)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(decrypted));
|
||||
Program.ArchiLogger.LogNullError(nameof(decrypted));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
return Convert.ToBase64String(encryptedData);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,11 +38,11 @@ namespace ArchiSteamFarm {
|
||||
internal sealed class DebugListener : IDebugListener {
|
||||
public void WriteLine(string category, string msg) {
|
||||
if (string.IsNullOrEmpty(category) && string.IsNullOrEmpty(msg)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(category) + " && " + nameof(msg));
|
||||
Program.ArchiLogger.LogNullError(nameof(category) + " && " + nameof(msg));
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericDebug(category + " | " + msg);
|
||||
Program.ArchiLogger.LogGenericDebug(category + " | " + msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo("No bots are running, exiting");
|
||||
Program.ArchiLogger.LogGenericInfo("No bots are running, exiting");
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
Program.Shutdown();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Weavers>
|
||||
<Costura IncludeDebugSymbols='false' />
|
||||
</Weavers>
|
||||
@@ -42,7 +42,7 @@ namespace ArchiSteamFarm {
|
||||
private const ushort DefaultWCFPort = 1242;
|
||||
|
||||
// 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, 480730 };
|
||||
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280, 480730, 566020 };
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool AutoRestart = true;
|
||||
@@ -59,9 +59,6 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly byte FarmingDelay = DefaultFarmingDelay;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool ForceHttp = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly byte GiftsLimiterDelay = 1;
|
||||
|
||||
@@ -102,14 +99,14 @@ namespace ArchiSteamFarm {
|
||||
internal readonly ushort WCFPort = DefaultWCFPort;
|
||||
|
||||
[JsonProperty]
|
||||
internal string WCFHostname { get; set; } = "localhost";
|
||||
internal string WCFHost { get; set; } = "127.0.0.1";
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private GlobalConfig() { }
|
||||
|
||||
internal static GlobalConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(filePath));
|
||||
Program.ArchiLogger.LogNullError(nameof(filePath));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -122,12 +119,12 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
globalConfig = JsonConvert.DeserializeObject<GlobalConfig>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (globalConfig == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(globalConfig));
|
||||
Program.ArchiLogger.LogNullError(nameof(globalConfig));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -138,24 +135,24 @@ namespace ArchiSteamFarm {
|
||||
case ProtocolType.Udp:
|
||||
break;
|
||||
default:
|
||||
ASF.ArchiLogger.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol);
|
||||
Program.ArchiLogger.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol);
|
||||
return null;
|
||||
}
|
||||
|
||||
// User might not know what he's doing
|
||||
// Ensure that he can't screw core ASF variables
|
||||
if (globalConfig.MaxFarmingTime == 0) {
|
||||
ASF.ArchiLogger.LogGenericWarning("Configured MaxFarmingTime is invalid: " + globalConfig.MaxFarmingTime);
|
||||
Program.ArchiLogger.LogGenericWarning("Configured MaxFarmingTime is invalid: " + globalConfig.MaxFarmingTime);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (globalConfig.FarmingDelay == 0) {
|
||||
ASF.ArchiLogger.LogGenericWarning("Configured FarmingDelay is invalid: " + globalConfig.FarmingDelay);
|
||||
Program.ArchiLogger.LogGenericWarning("Configured FarmingDelay is invalid: " + globalConfig.FarmingDelay);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (globalConfig.HttpTimeout == 0) {
|
||||
ASF.ArchiLogger.LogGenericWarning("Configured HttpTimeout is invalid: " + globalConfig.HttpTimeout);
|
||||
Program.ArchiLogger.LogGenericWarning("Configured HttpTimeout is invalid: " + globalConfig.HttpTimeout);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -163,7 +160,7 @@ namespace ArchiSteamFarm {
|
||||
return globalConfig;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort);
|
||||
Program.ArchiLogger.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
};
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly Guid Guid = Guid.NewGuid();
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider();
|
||||
|
||||
@@ -81,7 +84,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal static GlobalDatabase Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(filePath));
|
||||
Program.ArchiLogger.LogNullError(nameof(filePath));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -94,12 +97,12 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
globalDatabase = JsonConvert.DeserializeObject<GlobalDatabase>(File.ReadAllText(filePath), CustomSerializerSettings);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (globalDatabase == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(globalDatabase));
|
||||
Program.ArchiLogger.LogNullError(nameof(globalDatabase));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -112,7 +115,7 @@ namespace ArchiSteamFarm {
|
||||
private void Save() {
|
||||
string json = JsonConvert.SerializeObject(this, CustomSerializerSettings);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(json));
|
||||
Program.ArchiLogger.LogNullError(nameof(json));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -122,7 +125,7 @@ namespace ArchiSteamFarm {
|
||||
File.WriteAllText(FilePath, json);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
|
||||
Thread.Sleep(1000);
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
public Task UpdateServerListAsync(IEnumerable<IPEndPoint> endPoints) {
|
||||
if (endPoints == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(endPoints));
|
||||
Program.ArchiLogger.LogNullError(nameof(endPoints));
|
||||
return Task.Delay(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,25 +67,25 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
HtmlNode htmlNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='tradeoffer']");
|
||||
if (htmlNode == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(htmlNode));
|
||||
Program.ArchiLogger.LogNullError(nameof(htmlNode));
|
||||
return 0;
|
||||
}
|
||||
|
||||
string id = htmlNode.GetAttributeValue("id", null);
|
||||
if (string.IsNullOrEmpty(id)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(id));
|
||||
Program.ArchiLogger.LogNullError(nameof(id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int index = id.IndexOf('_');
|
||||
if (index < 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(index));
|
||||
Program.ArchiLogger.LogNullError(nameof(index));
|
||||
return 0;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (id.Length <= index) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(id.Length));
|
||||
Program.ArchiLogger.LogNullError(nameof(id.Length));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace ArchiSteamFarm.JSON {
|
||||
return _TradeOfferID;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogNullError(nameof(_TradeOfferID));
|
||||
Program.ArchiLogger.LogNullError(nameof(_TradeOfferID));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -131,13 +131,13 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
HtmlNode htmlNode = HtmlDocument.DocumentNode.SelectSingleNode("//a/@data-miniprofile");
|
||||
if (htmlNode == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(htmlNode));
|
||||
Program.ArchiLogger.LogNullError(nameof(htmlNode));
|
||||
return 0;
|
||||
}
|
||||
|
||||
string miniProfile = htmlNode.GetAttributeValue("data-miniprofile", null);
|
||||
if (string.IsNullOrEmpty(miniProfile)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(miniProfile));
|
||||
Program.ArchiLogger.LogNullError(nameof(miniProfile));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace ArchiSteamFarm.JSON {
|
||||
return _OtherSteamID3;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogNullError(nameof(_OtherSteamID3));
|
||||
Program.ArchiLogger.LogNullError(nameof(_OtherSteamID3));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -182,7 +182,7 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (value == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(value));
|
||||
Program.ArchiLogger.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -237,13 +237,13 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(value));
|
||||
Program.ArchiLogger.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
uint amount;
|
||||
if (!uint.TryParse(value, out amount) || (amount == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(amount));
|
||||
Program.ArchiLogger.LogNullError(nameof(amount));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -258,13 +258,13 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(value));
|
||||
Program.ArchiLogger.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
uint appID;
|
||||
if (!uint.TryParse(value, out appID) || (appID == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(appID));
|
||||
Program.ArchiLogger.LogNullError(nameof(appID));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -278,13 +278,13 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(value));
|
||||
Program.ArchiLogger.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
ulong assetID;
|
||||
if (!ulong.TryParse(value, out assetID) || (assetID == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(assetID));
|
||||
Program.ArchiLogger.LogNullError(nameof(assetID));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(value));
|
||||
Program.ArchiLogger.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -319,13 +319,13 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(value));
|
||||
Program.ArchiLogger.LogNullError(nameof(value));
|
||||
return;
|
||||
}
|
||||
|
||||
ulong contextID;
|
||||
if (!ulong.TryParse(value, out contextID) || (contextID == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(contextID));
|
||||
Program.ArchiLogger.LogNullError(nameof(contextID));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -359,19 +359,28 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
internal enum EType : byte {
|
||||
Unknown,
|
||||
|
||||
BoosterPack,
|
||||
Coupon,
|
||||
Gift,
|
||||
SteamGems,
|
||||
|
||||
Emoticon,
|
||||
Gift,
|
||||
FoilTradingCard,
|
||||
ProfileBackground,
|
||||
TradingCard
|
||||
TradingCard,
|
||||
SteamGems
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
internal sealed class NewDiscoveryQueueResponse { // Deserialized from JSON
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(PropertyName = "queue", Required = Required.Always)]
|
||||
internal readonly HashSet<uint> Queue;
|
||||
#pragma warning restore 649
|
||||
|
||||
private NewDiscoveryQueueResponse() { }
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
internal sealed class RedeemWalletResponse { // Deserialized from JSON
|
||||
@@ -396,7 +405,7 @@ namespace ArchiSteamFarm.JSON {
|
||||
}
|
||||
|
||||
if (OtherSteamID3 == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(OtherSteamID3));
|
||||
Program.ArchiLogger.LogNullError(nameof(OtherSteamID3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -440,7 +449,10 @@ namespace ArchiSteamFarm.JSON {
|
||||
foreach (Item item in ItemsToReceive) {
|
||||
Dictionary<Item.EType, uint> itemsPerType;
|
||||
if (!itemsToReceivePerGame.TryGetValue(item.RealAppID, out itemsPerType)) {
|
||||
itemsPerType = new Dictionary<Item.EType, uint> { [item.Type] = item.Amount };
|
||||
itemsPerType = new Dictionary<Item.EType, uint> {
|
||||
{ item.Type, item.Amount }
|
||||
};
|
||||
|
||||
itemsToReceivePerGame[item.RealAppID] = itemsPerType;
|
||||
} else {
|
||||
uint amount;
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static void OnConfigurationChanged(object sender, LoggingConfigurationChangedEventArgs e) {
|
||||
if ((sender == null) || (e == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
Program.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ using SteamKit2;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Program {
|
||||
internal static readonly ArchiLogger ArchiLogger = new ArchiLogger(SharedInfo.ASF);
|
||||
|
||||
internal static bool IsWCFRunning => WCF.IsServerRunning;
|
||||
internal static GlobalConfig GlobalConfig { get; private set; }
|
||||
internal static GlobalDatabase GlobalDatabase { get; private set; }
|
||||
@@ -59,7 +61,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (GlobalConfig.Headless || !Runtime.IsUserInteractive) {
|
||||
ASF.ArchiLogger.LogGenericWarning("Received a request for user input, but process is running in headless mode!");
|
||||
ArchiLogger.LogGenericWarning("Received a request for user input, but process is running in headless mode!");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -96,7 +98,7 @@ namespace ArchiSteamFarm {
|
||||
Console.Write("<" + botName + "> Please enter your 2 factor auth code from your authenticator app: ");
|
||||
break;
|
||||
case ASF.EUserInputType.WCFHostname:
|
||||
Console.Write("<" + botName + "> Please enter your WCF hostname: ");
|
||||
Console.Write("<" + botName + "> Please enter your WCF host: ");
|
||||
break;
|
||||
default:
|
||||
Console.Write("<" + botName + "> Please enter not documented yet value of \"" + userInputType + "\": ");
|
||||
@@ -123,7 +125,7 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
Process.Start(Assembly.GetEntryAssembly().Location, string.Join(" ", Environment.GetCommandLineArgs().Skip(1)));
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
|
||||
ShutdownResetEvent.Set();
|
||||
@@ -138,7 +140,7 @@ namespace ArchiSteamFarm {
|
||||
ShutdownResetEvent.Set();
|
||||
}
|
||||
|
||||
private static void Init(string[] args) {
|
||||
private static async Task Init(string[] args) {
|
||||
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
|
||||
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
|
||||
|
||||
@@ -169,10 +171,10 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Logging.InitLoggers();
|
||||
ASF.ArchiLogger.LogGenericInfo("ASF V" + SharedInfo.Version);
|
||||
ArchiLogger.LogGenericInfo("ASF V" + SharedInfo.Version);
|
||||
|
||||
if (!Runtime.IsRuntimeSupported) {
|
||||
ASF.ArchiLogger.LogGenericError("ASF detected unsupported runtime version, program might NOT run correctly in current environment. You're running it at your own risk!");
|
||||
ArchiLogger.LogGenericError("ASF detected unsupported runtime version, program might NOT run correctly in current environment. You're running it at your own risk!");
|
||||
Thread.Sleep(10000);
|
||||
}
|
||||
|
||||
@@ -193,7 +195,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// Parse post-init args
|
||||
if (args != null) {
|
||||
ParsePostInitArgs(args);
|
||||
await ParsePostInitArgs(args).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// If we ran ASF as a client, we're done by now
|
||||
@@ -201,8 +203,8 @@ namespace ArchiSteamFarm {
|
||||
Exit();
|
||||
}
|
||||
|
||||
ASF.CheckForUpdate().Wait();
|
||||
ASF.InitBots();
|
||||
await ASF.CheckForUpdate().ConfigureAwait(false);
|
||||
await ASF.InitBots().ConfigureAwait(false);
|
||||
ASF.InitFileWatcher();
|
||||
}
|
||||
|
||||
@@ -211,7 +213,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
GlobalConfig = GlobalConfig.Load(globalConfigFile);
|
||||
if (GlobalConfig == null) {
|
||||
ASF.ArchiLogger.LogGenericError("Global config could not be loaded, please make sure that " + globalConfigFile + " exists and is valid! Did you forget to read wiki?");
|
||||
ArchiLogger.LogGenericError("Global config could not be loaded, please make sure that " + globalConfigFile + " exists and is valid! Did you forget to read wiki?");
|
||||
Thread.Sleep(5000);
|
||||
Exit(1);
|
||||
}
|
||||
@@ -220,7 +222,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
GlobalDatabase = GlobalDatabase.Load(globalDatabaseFile);
|
||||
if (GlobalDatabase == null) {
|
||||
ASF.ArchiLogger.LogGenericError("Global database could not be loaded, if issue persists, please remove " + globalDatabaseFile + " in order to recreate database!");
|
||||
ArchiLogger.LogGenericError("Global database could not be loaded, if issue persists, please remove " + globalDatabaseFile + " in order to recreate database!");
|
||||
Thread.Sleep(5000);
|
||||
Exit(1);
|
||||
}
|
||||
@@ -229,7 +231,7 @@ namespace ArchiSteamFarm {
|
||||
WebBrowser.Init();
|
||||
WCF.Init();
|
||||
|
||||
WebBrowser = new WebBrowser(ASF.ArchiLogger);
|
||||
WebBrowser = new WebBrowser(ArchiLogger);
|
||||
}
|
||||
|
||||
private static bool InitShutdownSequence() {
|
||||
@@ -250,7 +252,7 @@ namespace ArchiSteamFarm {
|
||||
private static void Main(string[] args) {
|
||||
if (Runtime.IsUserInteractive) {
|
||||
// App
|
||||
Init(args);
|
||||
Init(args).Wait();
|
||||
|
||||
// Wait for signal to shutdown
|
||||
ShutdownResetEvent.Wait();
|
||||
@@ -266,9 +268,9 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParsePostInitArgs(IEnumerable<string> args) {
|
||||
private static async Task ParsePostInitArgs(IEnumerable<string> args) {
|
||||
if (args == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(args));
|
||||
ArchiLogger.LogNullError(nameof(args));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -282,7 +284,7 @@ namespace ArchiSteamFarm {
|
||||
case "--server":
|
||||
Mode |= EMode.Server;
|
||||
WCF.StartServer();
|
||||
ASF.InitBots();
|
||||
await ASF.InitBots().ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
if (arg.StartsWith("--", StringComparison.Ordinal)) {
|
||||
@@ -294,12 +296,11 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (!Mode.HasFlag(EMode.Client)) {
|
||||
ASF.ArchiLogger.LogGenericWarning("Ignoring command because --client wasn't specified: " + arg);
|
||||
ArchiLogger.LogGenericWarning("Ignoring command because --client wasn't specified: " + arg);
|
||||
break;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo("Command sent: " + arg);
|
||||
ASF.ArchiLogger.LogGenericInfo("Response received: " + WCF.SendCommand(arg));
|
||||
ArchiLogger.LogGenericInfo("Response received: " + WCF.SendCommand(arg));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -307,7 +308,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static void ParsePreInitArgs(IEnumerable<string> args) {
|
||||
if (args == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(args));
|
||||
ArchiLogger.LogNullError(nameof(args));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -335,20 +336,20 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
|
||||
if (args?.ExceptionObject == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(args) + " || " + nameof(args.ExceptionObject));
|
||||
ArchiLogger.LogNullError(nameof(args) + " || " + nameof(args.ExceptionObject));
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogFatalException((Exception) args.ExceptionObject);
|
||||
ArchiLogger.LogFatalException((Exception) args.ExceptionObject);
|
||||
}
|
||||
|
||||
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
|
||||
if (args?.Exception == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(args) + " || " + nameof(args.Exception));
|
||||
ArchiLogger.LogNullError(nameof(args) + " || " + nameof(args.Exception));
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogFatalException(args.Exception);
|
||||
ArchiLogger.LogFatalException(args.Exception);
|
||||
}
|
||||
|
||||
[Flags]
|
||||
@@ -363,8 +364,13 @@ namespace ArchiSteamFarm {
|
||||
ServiceName = SharedInfo.ServiceName;
|
||||
}
|
||||
|
||||
protected override void OnStart(string[] args) => Task.Run(() => {
|
||||
Init(args);
|
||||
protected override void OnStart(string[] args) => Task.Run(async () => {
|
||||
// Normally it'd make sense to use already provided string[] args parameter above
|
||||
// However, that one doesn't seem to work when ASF is started as a service, it's always null
|
||||
// Therefore, we will use Environment args in such case
|
||||
string[] envArgs = Environment.GetCommandLineArgs();
|
||||
await Init(envArgs).ConfigureAwait(false);
|
||||
|
||||
ShutdownResetEvent.Wait();
|
||||
Stop();
|
||||
});
|
||||
|
||||
@@ -39,38 +39,38 @@ namespace ArchiSteamFarm {
|
||||
if (IsRunningOnMono) {
|
||||
Version monoVersion = GetMonoVersion();
|
||||
if (monoVersion == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(monoVersion));
|
||||
Program.ArchiLogger.LogNullError(nameof(monoVersion));
|
||||
return false;
|
||||
}
|
||||
|
||||
Version minMonoVersion = new Version(4, 6);
|
||||
|
||||
if (monoVersion >= minMonoVersion) {
|
||||
ASF.ArchiLogger.LogGenericInfo("Your Mono version is OK. Required: " + minMonoVersion + " | Found: " + monoVersion);
|
||||
Program.ArchiLogger.LogGenericInfo("Your Mono version is OK. Required: " + minMonoVersion + " | Found: " + monoVersion);
|
||||
_IsRuntimeSupported = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericWarning("Your Mono version is too old. Required: " + minMonoVersion + " | Found: " + monoVersion);
|
||||
Program.ArchiLogger.LogGenericWarning("Your Mono version is too old. Required: " + minMonoVersion + " | Found: " + monoVersion);
|
||||
_IsRuntimeSupported = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Version netVersion = GetNetVersion();
|
||||
if (netVersion == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(netVersion));
|
||||
Program.ArchiLogger.LogNullError(nameof(netVersion));
|
||||
return false;
|
||||
}
|
||||
|
||||
Version minNetVersion = new Version(4, 6, 1);
|
||||
|
||||
if (netVersion >= minNetVersion) {
|
||||
ASF.ArchiLogger.LogGenericInfo("Your .NET version is OK. Required: " + minNetVersion + " | Found: " + netVersion);
|
||||
Program.ArchiLogger.LogGenericInfo("Your .NET version is OK. Required: " + minNetVersion + " | Found: " + netVersion);
|
||||
_IsRuntimeSupported = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericWarning("Your .NET version is too old. Required: " + minNetVersion + " | Found: " + netVersion);
|
||||
Program.ArchiLogger.LogGenericWarning("Your .NET version is too old. Required: " + minNetVersion + " | Found: " + netVersion);
|
||||
_IsRuntimeSupported = false;
|
||||
return false;
|
||||
}
|
||||
@@ -98,33 +98,34 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool RequiresTls12Testing => IsRunningOnMono;
|
||||
|
||||
private static readonly Type MonoRuntime = Type.GetType("Mono.Runtime");
|
||||
|
||||
private static bool? _IsRuntimeSupported;
|
||||
|
||||
private static bool? _IsUserInteractive;
|
||||
|
||||
private static Version GetMonoVersion() {
|
||||
if (MonoRuntime == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(MonoRuntime));
|
||||
Program.ArchiLogger.LogNullError(nameof(MonoRuntime));
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodInfo displayName = MonoRuntime.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (displayName == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(displayName));
|
||||
Program.ArchiLogger.LogNullError(nameof(displayName));
|
||||
return null;
|
||||
}
|
||||
|
||||
string versionString = (string) displayName.Invoke(null, null);
|
||||
if (string.IsNullOrEmpty(versionString)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(versionString));
|
||||
Program.ArchiLogger.LogNullError(nameof(versionString));
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = versionString.IndexOf(' ');
|
||||
if (index <= 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(index));
|
||||
Program.ArchiLogger.LogNullError(nameof(index));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ namespace ArchiSteamFarm {
|
||||
return version;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogNullError(nameof(version));
|
||||
Program.ArchiLogger.LogNullError(nameof(version));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -143,18 +144,18 @@ namespace ArchiSteamFarm {
|
||||
uint release;
|
||||
using (RegistryKey registryKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full\\")) {
|
||||
if (registryKey == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(registryKey));
|
||||
Program.ArchiLogger.LogNullError(nameof(registryKey));
|
||||
return null;
|
||||
}
|
||||
|
||||
object releaseObj = registryKey.GetValue("Release");
|
||||
if (releaseObj == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(releaseObj));
|
||||
Program.ArchiLogger.LogNullError(nameof(releaseObj));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!uint.TryParse(releaseObj.ToString(), out release) || (release == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(release));
|
||||
Program.ArchiLogger.LogNullError(nameof(release));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ namespace ArchiSteamFarm {
|
||||
internal const string LogFile = "log.txt";
|
||||
internal const string ServiceDescription = "ASF is an application that allows you to farm steam cards using multiple steam accounts simultaneously.";
|
||||
internal const string ServiceName = "ArchiSteamFarm";
|
||||
internal const string StatisticsServer = "https://asf.justarchi.net";
|
||||
internal const string VersionNumber = "2.1.7.1";
|
||||
internal const string StatisticsServer = "asf.justarchi.net";
|
||||
internal const string VersionNumber = "2.2.0.0";
|
||||
|
||||
internal static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SteamKit2;
|
||||
@@ -32,11 +33,21 @@ namespace ArchiSteamFarm {
|
||||
internal sealed class Statistics : IDisposable {
|
||||
private const byte MinHeartBeatTTL = 5; // Minimum amount of minutes we must wait before sending next HeartBeat
|
||||
|
||||
private static readonly SemaphoreSlim InitializationSemaphore = new SemaphoreSlim(1);
|
||||
|
||||
private static string _URL;
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
||||
|
||||
private bool HasAutomatedTrading => Bot.HasMobileAuthenticator && Bot.HasValidApiKey;
|
||||
private bool SteamTradeMatcher => Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher);
|
||||
|
||||
private string LastAvatarHash;
|
||||
private DateTime LastHeartBeat = DateTime.MinValue;
|
||||
private bool? LastMatchEverything;
|
||||
private string LastNickname;
|
||||
private bool ShouldSendHeartBeats;
|
||||
|
||||
internal Statistics(Bot bot) {
|
||||
if (bot == null) {
|
||||
@@ -49,20 +60,21 @@ namespace ArchiSteamFarm {
|
||||
public void Dispose() => Semaphore.Dispose();
|
||||
|
||||
internal async Task OnHeartBeat() {
|
||||
if (DateTime.Now < LastHeartBeat.AddMinutes(MinHeartBeatTTL)) {
|
||||
if (!ShouldSendHeartBeats || (DateTime.Now < LastHeartBeat.AddMinutes(MinHeartBeatTTL))) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (DateTime.Now < LastHeartBeat.AddMinutes(MinHeartBeatTTL)) {
|
||||
if (!ShouldSendHeartBeats || (DateTime.Now < LastHeartBeat.AddMinutes(MinHeartBeatTTL))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const string request = SharedInfo.StatisticsServer + "/api/HeartBeat";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
||||
{ "SteamID", Bot.SteamID.ToString() }
|
||||
string request = await GetURL().ConfigureAwait(false) + "/api/HeartBeat";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(2) {
|
||||
{ "SteamID", Bot.SteamID.ToString() },
|
||||
{ "Guid", Program.GlobalDatabase.Guid.ToString("N") }
|
||||
};
|
||||
|
||||
// We don't need retry logic here
|
||||
@@ -74,58 +86,110 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task OnLoggedOn() {
|
||||
await Bot.ArchiWebHandler.JoinGroup(SharedInfo.ASFGroupSteamID).ConfigureAwait(false);
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
const string request = SharedInfo.StatisticsServer + "/api/LoggedOn";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(4) {
|
||||
{ "SteamID", Bot.SteamID.ToString() },
|
||||
{ "HasAutomatedTrading", Bot.HasMobileAuthenticator && Bot.HasValidApiKey ? "1" : "0" },
|
||||
{ "SteamTradeMatcher", Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher) ? "1" : "0" },
|
||||
{ "MatchEverything", Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything) ? "1" : "0" }
|
||||
};
|
||||
|
||||
// We don't need retry logic here
|
||||
await Program.WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
||||
} finally {
|
||||
Semaphore.Release();
|
||||
}
|
||||
}
|
||||
internal async Task OnLoggedOn() => await Bot.ArchiWebHandler.JoinGroup(SharedInfo.ASFGroupSteamID).ConfigureAwait(false);
|
||||
|
||||
internal async Task OnPersonaState(SteamFriends.PersonaStateCallback callback) {
|
||||
if (callback == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(callback));
|
||||
Program.ArchiLogger.LogNullError(nameof(callback));
|
||||
return;
|
||||
}
|
||||
|
||||
string avatarHash = BitConverter.ToString(callback.AvatarHash).Replace("-", "").ToLowerInvariant();
|
||||
if (!string.IsNullOrEmpty(LastAvatarHash) && avatarHash.Equals(LastAvatarHash)) {
|
||||
// Don't announce if we don't meet conditions
|
||||
if (!HasAutomatedTrading || !SteamTradeMatcher) {
|
||||
ShouldSendHeartBeats = false;
|
||||
return;
|
||||
}
|
||||
|
||||
string nickname = callback.Name ?? "";
|
||||
string avatarHash = "";
|
||||
|
||||
if ((callback.AvatarHash != null) && (callback.AvatarHash.Length > 0) && callback.AvatarHash.Any(singleByte => singleByte != 0)) {
|
||||
avatarHash = BitConverter.ToString(callback.AvatarHash).Replace("-", "").ToLowerInvariant();
|
||||
if (avatarHash.Equals("0000000000000000000000000000000000000000")) {
|
||||
avatarHash = "";
|
||||
}
|
||||
}
|
||||
|
||||
bool matchEverything = Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything);
|
||||
|
||||
// Skip announcing if we already announced this bot with the same data
|
||||
if (!string.IsNullOrEmpty(LastNickname) && nickname.Equals(LastNickname) && !string.IsNullOrEmpty(LastAvatarHash) && avatarHash.Equals(LastAvatarHash) && LastMatchEverything.HasValue && (matchEverything == LastMatchEverything.Value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (!string.IsNullOrEmpty(LastAvatarHash) && avatarHash.Equals(LastAvatarHash)) {
|
||||
// Skip announcing if we already announced this bot with the same data
|
||||
if (!string.IsNullOrEmpty(LastNickname) && nickname.Equals(LastNickname) && !string.IsNullOrEmpty(LastAvatarHash) && avatarHash.Equals(LastAvatarHash) && LastMatchEverything.HasValue && (matchEverything == LastMatchEverything.Value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const string request = SharedInfo.StatisticsServer + "/api/PersonaState";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(2) {
|
||||
// Even if following request fails, we want to send HeartBeats regardless
|
||||
ShouldSendHeartBeats = true;
|
||||
|
||||
string request = await GetURL().ConfigureAwait(false) + "/api/Announce";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(5) {
|
||||
{ "SteamID", Bot.SteamID.ToString() },
|
||||
{ "AvatarHash", avatarHash }
|
||||
{ "Guid", Program.GlobalDatabase.Guid.ToString("N") },
|
||||
{ "Nickname", nickname },
|
||||
{ "AvatarHash", avatarHash },
|
||||
{ "MatchEverything", matchEverything ? "1" : "0" }
|
||||
};
|
||||
|
||||
// We don't need retry logic here
|
||||
if (await Program.WebBrowser.UrlPost(request, data).ConfigureAwait(false)) {
|
||||
LastNickname = nickname;
|
||||
LastAvatarHash = avatarHash;
|
||||
LastMatchEverything = matchEverything;
|
||||
}
|
||||
} finally {
|
||||
Semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> GetURL() {
|
||||
if (!string.IsNullOrEmpty(_URL)) {
|
||||
return _URL;
|
||||
}
|
||||
|
||||
// Our statistics server is using TLS 1.2 encryption method, which is not supported e.g. by older versions of Mono
|
||||
// That's not a problem, as we support HTTP too, but of course we prefer more secure HTTPS if possible
|
||||
// Because of that, this function is responsible for finding which URL should be used for accessing the server
|
||||
|
||||
// If our runtime doesn't require TLS 1.2 tests, skip the rest entirely, just use HTTPS
|
||||
const string httpsURL = "https://" + SharedInfo.StatisticsServer;
|
||||
if (!Runtime.RequiresTls12Testing) {
|
||||
_URL = httpsURL;
|
||||
return _URL;
|
||||
}
|
||||
|
||||
await InitializationSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (!string.IsNullOrEmpty(_URL)) {
|
||||
return _URL;
|
||||
}
|
||||
|
||||
// If we connect using HTTPS, use HTTPS as default version
|
||||
if (await Program.WebBrowser.UrlPost(httpsURL + "/api/ConnectionTest").ConfigureAwait(false)) {
|
||||
_URL = httpsURL;
|
||||
return _URL;
|
||||
}
|
||||
|
||||
// If we connect using HTTP, use HTTP as default version instead
|
||||
const string httpURL = "http://" + SharedInfo.StatisticsServer;
|
||||
if (await Program.WebBrowser.UrlPost(httpURL + "/api/ConnectionTest").ConfigureAwait(false)) {
|
||||
_URL = httpURL;
|
||||
return _URL;
|
||||
}
|
||||
} finally {
|
||||
InitializationSemaphore.Release();
|
||||
}
|
||||
|
||||
// If we didn't manage to establish connection through any of the above, return HTTPS, but don't record it
|
||||
// We might need to re-run this function in the future
|
||||
return httpsURL;
|
||||
}
|
||||
}
|
||||
}
|
||||
176
ArchiSteamFarm/SteamSaleEvent.cs
Normal file
176
ArchiSteamFarm/SteamSaleEvent.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using HtmlAgilityPack;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class SteamSaleEvent : IDisposable {
|
||||
private static readonly DateTime SaleEndingDateUtc = new DateTime(2017, 1, 2, 18, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly Timer SteamAwardsTimer;
|
||||
private readonly Timer SteamDiscoveryQueueTimer;
|
||||
|
||||
internal SteamSaleEvent(Bot bot) {
|
||||
if (bot == null) {
|
||||
throw new ArgumentNullException(nameof(bot));
|
||||
}
|
||||
|
||||
Bot = bot;
|
||||
|
||||
if (DateTime.UtcNow >= SaleEndingDateUtc) {
|
||||
return;
|
||||
}
|
||||
|
||||
SteamAwardsTimer = new Timer(
|
||||
async e => await VoteForSteamAwards().ConfigureAwait(false),
|
||||
null,
|
||||
TimeSpan.FromMinutes(1 + 0.2 * Bot.Bots.Count), // Delay
|
||||
TimeSpan.FromHours(6.1) // Period
|
||||
);
|
||||
|
||||
SteamDiscoveryQueueTimer = new Timer(
|
||||
async e => await ExploreDiscoveryQueue().ConfigureAwait(false),
|
||||
null,
|
||||
TimeSpan.FromMinutes(1 + 0.2 * Bot.Bots.Count), // Delay
|
||||
TimeSpan.FromHours(6.1) // Period
|
||||
);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
SteamAwardsTimer?.Dispose();
|
||||
SteamDiscoveryQueueTimer?.Dispose();
|
||||
}
|
||||
|
||||
private async Task ExploreDiscoveryQueue() {
|
||||
if (DateTime.UtcNow >= SaleEndingDateUtc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Bot.ArchiWebHandler.Ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericDebug("Started!");
|
||||
|
||||
for (byte i = 0; (i < 3) && !(await IsDiscoveryQueueEmpty().ConfigureAwait(false)).GetValueOrDefault(); i++) {
|
||||
Bot.ArchiLogger.LogGenericDebug("Getting new queue...");
|
||||
HashSet<uint> queue = await Bot.ArchiWebHandler.GenerateNewDiscoveryQueue().ConfigureAwait(false);
|
||||
if (queue == null) {
|
||||
Bot.ArchiLogger.LogGenericWarning("Aborting due to error!");
|
||||
break;
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericDebug("We got new queue, clearing...");
|
||||
foreach (uint queuedAppID in queue) {
|
||||
Bot.ArchiLogger.LogGenericDebug("Clearing " + queuedAppID + "...");
|
||||
if (await Bot.ArchiWebHandler.ClearFromDiscoveryQueue(queuedAppID).ConfigureAwait(false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericWarning("Aborting due to error!");
|
||||
i = byte.MaxValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericDebug("Done!");
|
||||
}
|
||||
|
||||
private async Task<bool?> IsDiscoveryQueueEmpty() {
|
||||
if (!Bot.ArchiWebHandler.Ready) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericDebug("Checking if discovery queue is empty...");
|
||||
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetDiscoveryQueuePage().ConfigureAwait(false);
|
||||
if (htmlDocument == null) {
|
||||
Bot.ArchiLogger.LogGenericDebug("Could not get discovery queue page, returning null");
|
||||
return null;
|
||||
}
|
||||
|
||||
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@class='subtext']");
|
||||
if (htmlNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(htmlNode));
|
||||
return null;
|
||||
}
|
||||
|
||||
string text = htmlNode.InnerText;
|
||||
if (!string.IsNullOrEmpty(text)) {
|
||||
// It'd make more sense to check "Come back tomorrow", but it might not cover out-of-the-event queue
|
||||
Bot.ArchiLogger.LogGenericDebug("Our text is: " + text);
|
||||
return !text.StartsWith("You can get ", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogNullError(nameof(text));
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task VoteForSteamAwards() {
|
||||
if (DateTime.UtcNow >= SaleEndingDateUtc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Bot.ArchiWebHandler.Ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericDebug("Getting SteamAwards page...");
|
||||
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetSteamAwardsPage().ConfigureAwait(false);
|
||||
|
||||
HtmlNodeCollection nominationsNodes = htmlDocument?.DocumentNode.SelectNodes("//div[@class='vote_nominations store_horizontal_autoslider']");
|
||||
if (nominationsNodes == null) {
|
||||
// Event ended, error or likewise
|
||||
Bot.ArchiLogger.LogGenericDebug("Could not get SteamAwards page, returning");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (HtmlNode nominationsNode in nominationsNodes) {
|
||||
HtmlNode myVoteNode = nominationsNode.SelectSingleNode("./div[@class='vote_nomination your_vote']");
|
||||
if (myVoteNode != null) {
|
||||
// Already voted
|
||||
Bot.ArchiLogger.LogGenericDebug("We voted already, nothing to do");
|
||||
continue;
|
||||
}
|
||||
|
||||
string voteIDText = nominationsNode.GetAttributeValue("data-voteid", null);
|
||||
if (string.IsNullOrEmpty(voteIDText)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(voteIDText));
|
||||
return;
|
||||
}
|
||||
|
||||
byte voteID;
|
||||
if (!byte.TryParse(voteIDText, out voteID) || (voteID == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(voteID));
|
||||
return;
|
||||
}
|
||||
|
||||
HtmlNodeCollection voteNodes = nominationsNode.SelectNodes("./div[@class='vote_nomination ']");
|
||||
if (voteNodes == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(voteNodes));
|
||||
return;
|
||||
}
|
||||
|
||||
// Random a game we'll actually vote for, we don't want to make GabeN angry by rigging votes...
|
||||
HtmlNode voteNode = voteNodes[Utilities.RandomNext(voteNodes.Count)];
|
||||
|
||||
string appIDText = voteNode.GetAttributeValue("data-vote-appid", null);
|
||||
if (string.IsNullOrEmpty(appIDText)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appIDText));
|
||||
return;
|
||||
}
|
||||
|
||||
uint appID;
|
||||
if (!uint.TryParse(appIDText, out appID) || (appID == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appID));
|
||||
return;
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericDebug("Voting in #" + voteID + " for " + appID + "...");
|
||||
await Bot.ArchiWebHandler.SteamAwardsVote(voteID, appID).ConfigureAwait(false);
|
||||
Bot.ArchiLogger.LogGenericDebug("Done!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ namespace ArchiSteamFarm {
|
||||
if (Bot.HasMobileAuthenticator) {
|
||||
HashSet<ulong> acceptedWithItemLoseTradeIDs = new HashSet<ulong>(results.Where(result => (result != null) && (result.Result == ParseTradeResult.EResult.AcceptedWithItemLose)).Select(result => result.TradeID));
|
||||
if (acceptedWithItemLoseTradeIDs.Count > 0) {
|
||||
await Task.Delay(1000).ConfigureAwait(false); // Sometimes we can be too fast for Steam servers to generate confirmations, wait a short moment
|
||||
await Task.Delay(3000).ConfigureAwait(false); // Sometimes we can be too fast for Steam servers to generate confirmations, wait a short moment
|
||||
await Bot.AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, 0, acceptedWithItemLoseTradeIDs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ namespace ArchiSteamFarm {
|
||||
// Now check if it's worth for us to do the trade
|
||||
await LimitInventoryRequestsAsync().ConfigureAwait(false);
|
||||
|
||||
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(false).ConfigureAwait(false);
|
||||
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(false, new HashSet<Steam.Item.EType> { Steam.Item.EType.TradingCard }).ConfigureAwait(false);
|
||||
if ((inventory == null) || (inventory.Count == 0)) {
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.AcceptedWithItemLose); // OK, assume that this trade is valid, we can't check our EQ
|
||||
}
|
||||
|
||||
@@ -30,13 +30,15 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Utilities {
|
||||
private static readonly Random Random = new Random();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
|
||||
internal static void Forget(this object obj) { }
|
||||
|
||||
internal static string GetCookieValue(this CookieContainer cookieContainer, string url, string name) {
|
||||
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(name)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(url) + " || " + nameof(name));
|
||||
Program.ArchiLogger.LogNullError(nameof(url) + " || " + nameof(name));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -45,14 +47,29 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
uri = new Uri(url);
|
||||
} catch (UriFormatException e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
CookieCollection cookies = cookieContainer.GetCookies(uri);
|
||||
return cookies.Count == 0 ? null : (from Cookie cookie in cookies where cookie.Name.Equals(name) select cookie.Value).FirstOrDefault();
|
||||
return cookies.Count != 0 ? (from Cookie cookie in cookies where cookie.Name.Equals(name) select cookie.Value).FirstOrDefault() : null;
|
||||
}
|
||||
|
||||
internal static uint GetUnixTime() => (uint) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
internal static uint GetUnixTime() => (uint) DateTimeOffset.Now.ToUnixTimeSeconds();
|
||||
|
||||
internal static int RandomNext(int maxWithout) {
|
||||
if (maxWithout <= 0) {
|
||||
Program.ArchiLogger.LogNullError(nameof(maxWithout));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (maxWithout == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock (Random) {
|
||||
return Random.Next(maxWithout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ using System;
|
||||
using System.Linq;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Description;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
[ServiceContract]
|
||||
@@ -39,7 +38,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
internal sealed class WCF : IWCF, IDisposable {
|
||||
private static string URL = "http://localhost:1242/ASF";
|
||||
private static string URL = "net.tcp://127.0.0.1:1242/ASF";
|
||||
|
||||
internal bool IsServerRunning => ServiceHost != null;
|
||||
|
||||
@@ -55,7 +54,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
public string HandleCommand(string input) {
|
||||
if (string.IsNullOrEmpty(input)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(input));
|
||||
Program.ArchiLogger.LogNullError(nameof(input));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -69,31 +68,36 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
string command = "!" + input;
|
||||
string output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo("Answered to command: " + input + " with: " + output);
|
||||
// TODO: This should be asynchronous, but for some reason Mono doesn't return any WCF output if it is
|
||||
// We must keep it synchronous until either Mono gets fixed, or culprit for freeze located (and corrected)
|
||||
string output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result;
|
||||
|
||||
Program.ArchiLogger.LogGenericInfo("Answered to WCF command: " + input + " with: " + output);
|
||||
return output;
|
||||
}
|
||||
|
||||
internal static void Init() {
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
|
||||
Program.GlobalConfig.WCFHostname = Program.GetUserInput(ASF.EUserInputType.WCFHostname);
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHost)) {
|
||||
Program.GlobalConfig.WCFHost = Program.GetUserInput(ASF.EUserInputType.WCFHostname);
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHost)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
|
||||
URL = "net.tcp://" + Program.GlobalConfig.WCFHost + ":" + Program.GlobalConfig.WCFPort + "/ASF";
|
||||
}
|
||||
|
||||
internal string SendCommand(string input) {
|
||||
if (string.IsNullOrEmpty(input)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(input));
|
||||
Program.ArchiLogger.LogNullError(nameof(input));
|
||||
return null;
|
||||
}
|
||||
|
||||
Program.ArchiLogger.LogGenericInfo("Sending command: " + input + " to WCF server on " + URL + "...");
|
||||
|
||||
if (Client == null) {
|
||||
Client = new Client(new BasicHttpBinding(), new EndpointAddress(URL));
|
||||
Client = new Client(new NetTcpBinding { Security = { Mode = SecurityMode.None } }, new EndpointAddress(URL));
|
||||
}
|
||||
|
||||
return Client.HandleCommand(input);
|
||||
@@ -104,23 +108,19 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo("Starting WCF server...");
|
||||
Program.ArchiLogger.LogGenericInfo("Starting WCF server on " + URL + "...");
|
||||
|
||||
try {
|
||||
ServiceHost = new ServiceHost(typeof(WCF), new Uri(URL));
|
||||
|
||||
ServiceHost.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
|
||||
|
||||
ServiceHost.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
|
||||
ServiceHost.AddServiceEndpoint(typeof(IWCF), new BasicHttpBinding(), string.Empty);
|
||||
|
||||
ServiceHost.AddServiceEndpoint(typeof(IWCF), new NetTcpBinding { Security = { Mode = SecurityMode.None } }, string.Empty);
|
||||
ServiceHost.Open();
|
||||
ASF.ArchiLogger.LogGenericInfo("WCF server ready!");
|
||||
|
||||
Program.ArchiLogger.LogGenericInfo("WCF server ready!");
|
||||
} catch (AddressAccessDeniedException) {
|
||||
ASF.ArchiLogger.LogGenericError("WCF service could not be started because of AddressAccessDeniedException!");
|
||||
ASF.ArchiLogger.LogGenericWarning("If you want to use WCF service provided by ASF, consider starting ASF as administrator, or giving proper permissions!");
|
||||
Program.ArchiLogger.LogGenericError("WCF service could not be started because of AddressAccessDeniedException!");
|
||||
Program.ArchiLogger.LogGenericWarning("If you want to use WCF service provided by ASF, consider starting ASF as administrator, or giving proper permissions!");
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
ServiceHost.Close();
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,14 +158,14 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal string HandleCommand(string input) {
|
||||
if (string.IsNullOrEmpty(input)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(input));
|
||||
Program.ArchiLogger.LogNullError(nameof(input));
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return Channel.HandleCommand(input);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Program.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning("Request failed even after " + MaxRetries + " tries");
|
||||
ArchiLogger.LogGenericDebug("Failing request: " + request);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -121,6 +122,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning("Request failed even after " + MaxRetries + " tries");
|
||||
ArchiLogger.LogGenericDebug("Failing request: " + request);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -140,6 +142,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning("Request failed even after " + MaxRetries + " tries");
|
||||
ArchiLogger.LogGenericDebug("Failing request: " + request);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -178,6 +181,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning("Request failed even after " + MaxRetries + " tries");
|
||||
ArchiLogger.LogGenericDebug("Failing request: " + request);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -197,6 +201,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning("Request failed even after " + MaxRetries + " tries");
|
||||
ArchiLogger.LogGenericDebug("Failing request: " + request);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -216,6 +221,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning("Request failed even after " + MaxRetries + " tries");
|
||||
ArchiLogger.LogGenericDebug("Failing request: " + request);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -246,6 +252,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning("Request failed even after " + MaxRetries + " tries");
|
||||
ArchiLogger.LogGenericDebug("Failing request: " + request);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -334,6 +341,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning("Request failed even after " + MaxRetries + " tries");
|
||||
ArchiLogger.LogGenericDebug("Failing request: " + request);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -470,6 +478,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning("Request failed even after " + MaxRetries + " tries");
|
||||
ArchiLogger.LogGenericDebug("Failing request: " + request);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -488,10 +497,6 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (request.StartsWith("https://", StringComparison.Ordinal) && Program.GlobalConfig.ForceHttp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpResponseMessage responseMessage;
|
||||
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
|
||||
if (data != null) {
|
||||
@@ -512,7 +517,7 @@ namespace ArchiSteamFarm {
|
||||
} catch (Exception e) {
|
||||
// This exception is really common, don't bother with it unless debug mode is enabled
|
||||
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
ArchiLogger.LogGenericDebugException(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -528,9 +533,9 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
||||
ArchiLogger.LogGenericError("Request: " + request + " failed!");
|
||||
ArchiLogger.LogGenericError("Status code: " + responseMessage.StatusCode);
|
||||
ArchiLogger.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false));
|
||||
ArchiLogger.LogGenericDebug("Request: " + request + " failed!");
|
||||
ArchiLogger.LogGenericDebug("Status code: " + responseMessage.StatusCode);
|
||||
ArchiLogger.LogGenericDebug("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false));
|
||||
}
|
||||
|
||||
responseMessage.Dispose();
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"Debug": false,
|
||||
"Headless": false,
|
||||
"AutoUpdates": true,
|
||||
"AutoRestart": true,
|
||||
"UpdateChannel": 1,
|
||||
"SteamProtocol": 6,
|
||||
"SteamOwnerID": 0,
|
||||
"MaxFarmingTime": 10,
|
||||
"IdleFarmingPeriod": 3,
|
||||
"FarmingDelay": 15,
|
||||
"LoginLimiterDelay": 10,
|
||||
"InventoryLimiterDelay": 3,
|
||||
"GiftsLimiterDelay": 1,
|
||||
"MaxTradeHoldDuration": 15,
|
||||
"ForceHttp": false,
|
||||
"HttpTimeout": 60,
|
||||
"WCFHostname": "localhost",
|
||||
"WCFPort": 1242,
|
||||
"Statistics": true,
|
||||
"Blacklist": [
|
||||
267420,
|
||||
303700,
|
||||
335590,
|
||||
368020,
|
||||
425280,
|
||||
480730
|
||||
]
|
||||
}
|
||||
"AutoRestart": true,
|
||||
"AutoUpdates": true,
|
||||
"Blacklist": [
|
||||
267420,
|
||||
303700,
|
||||
335590,
|
||||
368020,
|
||||
425280,
|
||||
480730,
|
||||
566020
|
||||
],
|
||||
"Debug": false,
|
||||
"FarmingDelay": 15,
|
||||
"GiftsLimiterDelay": 1,
|
||||
"Headless": false,
|
||||
"HttpTimeout": 60,
|
||||
"IdleFarmingPeriod": 3,
|
||||
"InventoryLimiterDelay": 3,
|
||||
"LoginLimiterDelay": 10,
|
||||
"MaxFarmingTime": 10,
|
||||
"MaxTradeHoldDuration": 15,
|
||||
"Statistics": true,
|
||||
"SteamOwnerID": 0,
|
||||
"SteamProtocol": 6,
|
||||
"UpdateChannel": 1,
|
||||
"WCFHost": "127.0.0.1",
|
||||
"WCFPort": 1242
|
||||
}
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
{
|
||||
"Enabled": false,
|
||||
"Paused": false,
|
||||
"SteamLogin": null,
|
||||
"SteamPassword": null,
|
||||
"PasswordFormat": 0,
|
||||
"SteamParentalPIN": "0",
|
||||
"SteamApiKey": null,
|
||||
"SteamMasterID": 0,
|
||||
"SteamMasterClanID": 0,
|
||||
"CardDropsRestricted": true,
|
||||
"DismissInventoryNotifications": true,
|
||||
"FarmingOrder": 0,
|
||||
"FarmOffline": false,
|
||||
"HandleOfflineMessages": false,
|
||||
"AcceptGifts": false,
|
||||
"IsBotAccount": false,
|
||||
"ForwardKeysToOtherBots": false,
|
||||
"DistributeKeys": false,
|
||||
"ShutdownOnFarmingFinished": false,
|
||||
"SendOnFarmingFinished": false,
|
||||
"SteamTradeToken": null,
|
||||
"SendTradePeriod": 0,
|
||||
"TradingPreferences": 1,
|
||||
"AcceptConfirmationsPeriod": 0,
|
||||
"CustomGamePlayedWhileFarming": null,
|
||||
"CustomGamePlayedWhileIdle": null,
|
||||
"GamesPlayedWhileIdle": []
|
||||
}
|
||||
"AcceptConfirmationsPeriod": 0,
|
||||
"AcceptGifts": false,
|
||||
"CardDropsRestricted": true,
|
||||
"CustomGamePlayedWhileFarming": null,
|
||||
"CustomGamePlayedWhileIdle": null,
|
||||
"DismissInventoryNotifications": true,
|
||||
"Enabled": false,
|
||||
"FarmingOrder": 0,
|
||||
"FarmOffline": false,
|
||||
"GamesPlayedWhileIdle": [],
|
||||
"HandleOfflineMessages": false,
|
||||
"IsBotAccount": false,
|
||||
"LootableTypes": [
|
||||
1,
|
||||
5,
|
||||
7
|
||||
],
|
||||
"PasswordFormat": 0,
|
||||
"Paused": false,
|
||||
"RedeemingPreferences": 0,
|
||||
"SendOnFarmingFinished": false,
|
||||
"SendTradePeriod": 0,
|
||||
"ShutdownOnFarmingFinished": false,
|
||||
"SteamApiKey": null,
|
||||
"SteamLogin": null,
|
||||
"SteamMasterClanID": 0,
|
||||
"SteamMasterID": 0,
|
||||
"SteamParentalPIN": "0",
|
||||
"SteamPassword": null,
|
||||
"SteamTradeToken": null,
|
||||
"TradingPreferences": 1
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"Enabled": false,
|
||||
"SteamLogin": null,
|
||||
"SteamPassword": null
|
||||
}
|
||||
"Enabled": false,
|
||||
"SteamLogin": null,
|
||||
"SteamPassword": null
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing.Design;
|
||||
using System.IO;
|
||||
using ConfigGenerator.JSON;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
@@ -56,9 +57,6 @@ namespace ConfigGenerator {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool DismissInventoryNotifications { get; set; } = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool DistributeKeys { get; set; } = false;
|
||||
|
||||
[Category("\t\tCore")]
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Enabled { get; set; } = false;
|
||||
@@ -69,9 +67,6 @@ namespace ConfigGenerator {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool FarmOffline { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool ForwardKeysToOtherBots { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public List<uint> GamesPlayedWhileIdle { get; set; } = new List<uint>();
|
||||
|
||||
@@ -83,6 +78,9 @@ namespace ConfigGenerator {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool IsBotAccount { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public List<Steam.Item.EType> LootableTypes { get; set; } = new List<Steam.Item.EType> { Steam.Item.EType.BoosterPack, Steam.Item.EType.FoilTradingCard, Steam.Item.EType.TradingCard };
|
||||
|
||||
[Category("\tAccess")]
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ECryptoMethod PasswordFormat { get; set; } = ECryptoMethod.PlainText;
|
||||
@@ -91,6 +89,11 @@ namespace ConfigGenerator {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Paused { get; set; } = false;
|
||||
|
||||
[Category("\tAdvanced")]
|
||||
[Editor(typeof(FlagEnumUiEditor), typeof(UITypeEditor))]
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ERedeemingPreferences RedeemingPreferences { get; set; } = ERedeemingPreferences.None;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool SendOnFarmingFinished { get; set; } = false;
|
||||
|
||||
@@ -191,6 +194,13 @@ namespace ConfigGenerator {
|
||||
NamesDescending
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum ERedeemingPreferences : byte {
|
||||
None = 0,
|
||||
Forwarding = 1,
|
||||
Distributing = 2
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum ETradingPreferences : byte {
|
||||
None = 0,
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
<Compile Include="ConfigPage.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="JSON\Steam.cs" />
|
||||
<Compile Include="Logging.cs" />
|
||||
<Compile Include="MainForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace ConfigGenerator {
|
||||
private const ushort DefaultWCFPort = 1242;
|
||||
|
||||
// This is hardcoded blacklist which should not be possible to change
|
||||
private static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280, 480730 };
|
||||
private static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280, 480730, 566020 };
|
||||
|
||||
[Category("\tUpdates")]
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
@@ -64,10 +64,6 @@ namespace ConfigGenerator {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte FarmingDelay { get; set; } = DefaultFarmingDelay;
|
||||
|
||||
[Category("\tDebugging")]
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool ForceHttp { get; set; } = false;
|
||||
|
||||
[Category("\tPerformance")]
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte GiftsLimiterDelay { get; set; } = 1;
|
||||
@@ -116,7 +112,7 @@ namespace ConfigGenerator {
|
||||
|
||||
[Category("\tAccess")]
|
||||
[JsonProperty]
|
||||
public string WCFHostname { get; set; } = "localhost";
|
||||
public string WCFHost { get; set; } = "127.0.0.1";
|
||||
|
||||
[Category("\tAccess")]
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
|
||||
44
ConfigGenerator/JSON/Steam.cs
Normal file
44
ConfigGenerator/JSON/Steam.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ConfigGenerator.JSON {
|
||||
internal static class Steam {
|
||||
internal static class Item {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal enum EType : byte {
|
||||
Unknown,
|
||||
BoosterPack,
|
||||
Coupon,
|
||||
Emoticon,
|
||||
Gift,
|
||||
FoilTradingCard,
|
||||
ProfileBackground,
|
||||
TradingCard,
|
||||
SteamGems
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,12 +31,18 @@ namespace GUI {
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback.AvatarHash != null) {
|
||||
string avatarHash = BitConverter.ToString(callback.AvatarHash).Replace("-", "").ToLowerInvariant();
|
||||
string avatarURL = "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/" + avatarHash.Substring(0, 2) + "/" + avatarHash + "_full.jpg";
|
||||
AvatarPictureBox.ImageLocation = avatarURL;
|
||||
AvatarPictureBox.LoadAsync();
|
||||
if ((callback.AvatarHash == null) || (callback.AvatarHash.Length == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string avatarHash = BitConverter.ToString(callback.AvatarHash).Replace("-", "").ToLowerInvariant();
|
||||
if (string.IsNullOrEmpty(avatarHash)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string avatarURL = "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/" + avatarHash.Substring(0, 2) + "/" + avatarHash + "_full.jpg";
|
||||
AvatarPictureBox.ImageLocation = avatarURL;
|
||||
AvatarPictureBox.LoadAsync();
|
||||
}
|
||||
|
||||
private void AvatarPictureBox_LoadCompleted(object sender, AsyncCompletedEventArgs e) {
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal static void OnPersonaState(Bot bot, SteamFriends.PersonaStateCallback callback) {
|
||||
if (bot == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(bot));
|
||||
Program.ArchiLogger.LogNullError(nameof(bot));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,9 @@
|
||||
<Compile Include="..\ArchiSteamFarm\CardsFarmer.cs">
|
||||
<Link>CardsFarmer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ArchiSteamFarm\CMsgs\CMsgClientClanInviteAction.cs">
|
||||
<Link>CMsgs\CMsgClientClanInviteAction.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ArchiSteamFarm\ConcurrentEnumerator.cs">
|
||||
<Link>ConcurrentEnumerator.cs</Link>
|
||||
</Compile>
|
||||
@@ -148,6 +151,9 @@
|
||||
<Compile Include="..\ArchiSteamFarm\Statistics.cs">
|
||||
<Link>Statistics.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ArchiSteamFarm\SteamSaleEvent.cs">
|
||||
<Link>SteamSaleEvent.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ArchiSteamFarm\Trading.cs">
|
||||
<Link>Trading.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace GUI {
|
||||
|
||||
internal static void UpdateBotAvatar(string botName, Image image) {
|
||||
if (string.IsNullOrEmpty(botName) || (image == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(image));
|
||||
Program.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(image));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,17 +68,17 @@ namespace GUI {
|
||||
BotListView.LargeImageList = BotListView.SmallImageList = AvatarImageList;
|
||||
|
||||
await Task.Run(async () => {
|
||||
ASF.ArchiLogger.LogGenericInfo("ASF V" + SharedInfo.Version);
|
||||
Program.ArchiLogger.LogGenericInfo("ASF V" + SharedInfo.Version);
|
||||
|
||||
if (!Directory.Exists(SharedInfo.ConfigDirectory)) {
|
||||
ASF.ArchiLogger.LogGenericError("Config directory could not be found!");
|
||||
Program.ArchiLogger.LogGenericError("Config directory could not be found!");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
await ASF.CheckForUpdate().ConfigureAwait(false);
|
||||
|
||||
// Before attempting to connect, initialize our list of CMs
|
||||
Bot.InitializeCMs(Program.GlobalDatabase.CellID, Program.GlobalDatabase.ServerListProvider);
|
||||
await Bot.InitializeCMs(Program.GlobalDatabase.CellID, Program.GlobalDatabase.ServerListProvider).ConfigureAwait(false);
|
||||
});
|
||||
|
||||
foreach (string botName in Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension)) {
|
||||
@@ -133,7 +133,7 @@ namespace GUI {
|
||||
|
||||
private static Bitmap ResizeImage(Image image, int width, int height) {
|
||||
if ((image == null) || (width <= 0) || (height <= 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(image) + " || " + nameof(width) + " || " + nameof(height));
|
||||
Program.ArchiLogger.LogNullError(nameof(image) + " || " + nameof(width) + " || " + nameof(height));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ using SteamKit2;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Program {
|
||||
internal static readonly ArchiLogger ArchiLogger = new ArchiLogger(SharedInfo.ASF);
|
||||
|
||||
internal static GlobalConfig GlobalConfig { get; private set; }
|
||||
internal static GlobalDatabase GlobalDatabase { get; private set; }
|
||||
internal static WebBrowser WebBrowser { get; private set; }
|
||||
@@ -38,7 +40,7 @@ namespace ArchiSteamFarm {
|
||||
try {
|
||||
Process.Start(Assembly.GetEntryAssembly().Location, string.Join(" ", Environment.GetCommandLineArgs().Skip(1)));
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
|
||||
Environment.Exit(0);
|
||||
@@ -51,7 +53,7 @@ namespace ArchiSteamFarm {
|
||||
Logging.InitCoreLoggers();
|
||||
|
||||
if (!Runtime.IsRuntimeSupported) {
|
||||
ASF.ArchiLogger.LogGenericError("ASF detected unsupported runtime version, program might NOT run correctly in current environment. You're running it at your own risk!");
|
||||
ArchiLogger.LogGenericError("ASF detected unsupported runtime version, program might NOT run correctly in current environment. You're running it at your own risk!");
|
||||
}
|
||||
|
||||
string homeDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
@@ -101,7 +103,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
GlobalConfig = GlobalConfig.Load(globalConfigFile);
|
||||
if (GlobalConfig == null) {
|
||||
ASF.ArchiLogger.LogGenericError("Global config could not be loaded, please make sure that " + globalConfigFile + " exists and is valid!");
|
||||
ArchiLogger.LogGenericError("Global config could not be loaded, please make sure that " + globalConfigFile + " exists and is valid!");
|
||||
Exit(1);
|
||||
}
|
||||
|
||||
@@ -109,14 +111,14 @@ namespace ArchiSteamFarm {
|
||||
|
||||
GlobalDatabase = GlobalDatabase.Load(globalDatabaseFile);
|
||||
if (GlobalDatabase == null) {
|
||||
ASF.ArchiLogger.LogGenericError("Global database could not be loaded, if issue persists, please remove " + globalDatabaseFile + " in order to recreate database!");
|
||||
ArchiLogger.LogGenericError("Global database could not be loaded, if issue persists, please remove " + globalDatabaseFile + " in order to recreate database!");
|
||||
Exit(1);
|
||||
}
|
||||
|
||||
ArchiWebHandler.Init();
|
||||
WebBrowser.Init();
|
||||
|
||||
WebBrowser = new WebBrowser(ASF.ArchiLogger);
|
||||
WebBrowser = new WebBrowser(ArchiLogger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -132,20 +134,20 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
|
||||
if (args?.ExceptionObject == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(args) + " || " + nameof(args.ExceptionObject));
|
||||
ArchiLogger.LogNullError(nameof(args) + " || " + nameof(args.ExceptionObject));
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogFatalException((Exception) args.ExceptionObject);
|
||||
ArchiLogger.LogFatalException((Exception) args.ExceptionObject);
|
||||
}
|
||||
|
||||
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
|
||||
if (args?.Exception == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(args) + " || " + nameof(args.Exception));
|
||||
ArchiLogger.LogNullError(nameof(args) + " || " + nameof(args.Exception));
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogFatalException(args.Exception);
|
||||
ArchiLogger.LogFatalException(args.Exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user