Compare commits

...

32 Commits

Author SHA1 Message Date
JustArchi
44b3a518bd 5 seconds seems good for now 2015-11-04 18:30:38 +01:00
JustArchi
1fe60e7037 Trim GetUserInput(), closes #12 2015-11-04 18:18:04 +01:00
JustArchi
27ad98492f Misc 2015-11-04 05:01:18 +01:00
JustArchi
e3386c5b29 Misc 2015-11-04 04:55:25 +01:00
JustArchi
021ac470d3 Handle broken configs 2015-11-04 04:51:52 +01:00
JustArchi
b4fb0bf9a6 Update defaults according to example.xml changes 2015-11-04 04:46:55 +01:00
JustArchi
b63dd1035c Improve flow and get rid of synchronous sleeps 2015-11-04 04:42:13 +01:00
JustArchi
f577e1a6cb Bots are now limited in Start() 2015-11-04 04:35:34 +01:00
JustArchi
34ffb975b6 Use new experimental delay everywhere 2015-11-04 04:32:26 +01:00
Łukasz Domeradzki
5a3b132d5e Merge pull request #11 from Pandiora/patch-6
Random Number for delayed connecting bots
2015-11-04 04:19:30 +01:00
Pandi
ef29b6f33d Random Number for delayed connecting bots
See my answer @ #10
2015-11-03 19:06:50 +01:00
JustArchi
e8335ac183 EXPERIMENTAL: Try to solve #10 2015-11-03 09:34:35 +01:00
JustArchi
90339fb276 Merge branch 'master' of https://github.com/JustArchi/ArchiSteamFarm 2015-11-03 00:47:23 +01:00
JustArchi
88759303dd Add more ignored IDs 2015-11-03 00:47:21 +01:00
Łukasz Domeradzki
befe01ca59 Merge pull request #8 from Pandiora/patch-5
Added Debian 8.1 (tested and working)
2015-11-02 02:12:52 +01:00
Pandi
c9cc728da3 Jessi > Jessie - fixed 2015-11-01 22:27:38 +01:00
Pandi
a81a59396d Added Debian 8.1 (tested and working) 2015-11-01 22:25:57 +01:00
Łukasz Domeradzki
df3fcfb1df Merge pull request #7 from Pandiora/patch-4
Cleaned up better looking Readme
2015-11-01 18:57:25 +01:00
Pandi
e097b33d89 Cleaned up better looking Readme
Removed the part of smart multi-game-idling where you wrote about the 2 hour-restriction of steam. AFAIK this restriction doesn´t exists anymore and you can farm cards on every game directly after you bought it. Added new sections filled with data I´m sure bout:

- Current Commands
- Operating Systems

Also rewrote some text-fragments or edited the format. Hope this looks good for you too and helps.
2015-11-01 18:55:47 +01:00
Łukasz Domeradzki
db6e775b10 Merge pull request #6 from Pandiora/patch-3
SteamMasterID Change
2015-11-01 18:01:16 +01:00
Pandiora
b53c41a0f9 removed newline
¯\_(ツ)_/¯
2015-11-01 18:01:07 +01:00
Pandiora
fec4873843 SteamID64 - changed invalid to 0
check
2015-11-01 17:58:52 +01:00
Pandiora
1e2efe38f1 SteamMasterID Change
To avoid ppl using your SteamID by accident, replaced it  with "00000000000000000" and added yours as example.
2015-11-01 17:56:44 +01:00
JustArchi
3e0dfac091 Misc 2015-11-01 17:53:38 +01:00
Łukasz Domeradzki
36d745aece Merge pull request #5 from Pandiora/patch-2
Explanation about how to find the groupID64
2015-11-01 17:46:56 +01:00
Pandiora
7896e7924e Explanation about how to find the groupID64
Didn´t added this to Wiki, because explanation is relatively short.
2015-11-01 17:45:19 +01:00
JustArchi
5b3c730d51 Ship items together with their IDs 2015-11-01 16:53:08 +01:00
JustArchi
f3aee0c34f Add support for activated games names 2015-11-01 16:46:02 +01:00
JustArchi
47389f67b8 One more status 2015-11-01 05:21:18 +01:00
JustArchi
19e72c93c1 Bugfix 2015-11-01 02:08:41 +01:00
JustArchi
164240641b Work work 2015-11-01 02:04:44 +01:00
JustArchi
337c397505 0.6 2015-10-31 09:17:56 +01:00
11 changed files with 366 additions and 179 deletions

View File

@@ -24,6 +24,8 @@
using SteamKit2; using SteamKit2;
using SteamKit2.Internal; using SteamKit2.Internal;
using System.Collections.Generic;
using System.IO;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal sealed class ArchiHandler : ClientMsgHandler { internal sealed class ArchiHandler : ClientMsgHandler {
@@ -33,6 +35,7 @@ namespace ArchiSteamFarm {
Unknown = -1, Unknown = -1,
OK = 0, OK = 0,
AlreadyOwned = 9, AlreadyOwned = 9,
RegionLockedKey = 13,
InvalidKey = 14, InvalidKey = 14,
DuplicatedKey = 15, DuplicatedKey = 15,
BaseGameRequired = 24, BaseGameRequired = 24,
@@ -41,14 +44,20 @@ namespace ArchiSteamFarm {
internal EResult Result { get; private set; } internal EResult Result { get; private set; }
internal EPurchaseResult PurchaseResult { get; private set; } internal EPurchaseResult PurchaseResult { get; private set; }
internal int ErrorCode { get; private set; } internal KeyValue ReceiptInfo { get; private set; } = new KeyValue();
internal byte[] ReceiptInfo { get; private set; } internal Dictionary<uint, string> Items { get; private set; } = new Dictionary<uint, string>();
internal PurchaseResponseCallback(CMsgClientPurchaseResponse body) { internal PurchaseResponseCallback(CMsgClientPurchaseResponse body) {
Result = (EResult) body.eresult; Result = (EResult) body.eresult;
ErrorCode = body.purchase_result_details; PurchaseResult = (EPurchaseResult) body.purchase_result_details;
ReceiptInfo = body.purchase_receipt_info;
PurchaseResult = (EPurchaseResult) ErrorCode; using (MemoryStream ms = new MemoryStream(body.purchase_receipt_info)) {
if (ReceiptInfo.TryReadAsBinary(ms)) {
foreach (KeyValue lineItem in ReceiptInfo["lineitems"].Children) {
Items.Add((uint) lineItem["PackageID"].AsUnsignedLong(), lineItem["ItemDescription"].AsString());
}
}
}
} }
} }

View File

@@ -54,6 +54,9 @@
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>cirno.ico</ApplicationIcon> <ApplicationIcon>cirno.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="HtmlAgilityPack, Version=1.4.9.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL"> <Reference Include="HtmlAgilityPack, Version=1.4.9.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
<HintPath>..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll</HintPath> <HintPath>..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll</HintPath>
@@ -113,22 +116,23 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="cirno.ico" /> <Content Include="cirno.ico" />
<Content Include="config\example.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent Condition=" '$(OS)' != 'Unix' ">(robocopy $(ProjectDir)config $(TargetDir)config /S /E) ^&amp; IF %25ERRORLEVEL%25 GEQ 2 exit 0 <PreBuildEvent>
</PreBuildEvent>
if $(ConfigurationName) == Release ( </PropertyGroup>
<PropertyGroup>
<PostBuildEvent Condition=" '$(OS)' != 'Unix' ">if $(ConfigurationName) == Release (
mkdir "$(TargetDir)out" "$(TargetDir)out\config" mkdir "$(TargetDir)out" "$(TargetDir)out\config"
copy "$(TargetDir)config\example.xml" "$(TargetDir)out\config" copy "$(TargetDir)config\example.xml" "$(TargetDir)out\config"
"$(SolutionDir)tools\ILMerge.exe" /out:"$(TargetDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards "$(SolutionDir)tools\ILMerge.exe" /out:"$(TargetDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards
del "$(TargetDir)out\ASF.pdb" del "$(TargetDir)out\ASF.pdb"
)</PostBuildEvent> )</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -27,7 +27,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
@@ -35,7 +34,7 @@ namespace ArchiSteamFarm {
internal class Bot { internal class Bot {
private const ushort CallbackSleep = 500; // In miliseconds private const ushort CallbackSleep = 500; // In miliseconds
private static readonly HashSet<Bot> Bots = new HashSet<Bot>(); private static readonly Dictionary<string, Bot> Bots = new Dictionary<string, Bot>();
private readonly string ConfigFile; private readonly string ConfigFile;
private readonly string SentryFile; private readonly string SentryFile;
@@ -55,16 +54,16 @@ namespace ArchiSteamFarm {
internal Trading Trading { get; private set; } internal Trading Trading { get; private set; }
// Config variables // Config variables
internal bool Enabled { get; private set; } = true; internal bool Enabled { get; private set; } = false;
internal string SteamLogin { get; private set; } = "null"; internal string SteamLogin { get; private set; } = "null";
internal string SteamPassword { get; private set; } = "null"; internal string SteamPassword { get; private set; } = "null";
internal string SteamNickname { get; private set; } = "null"; internal string SteamNickname { get; private set; } = "null";
internal string SteamApiKey { get; private set; } = "null"; internal string SteamApiKey { get; private set; } = "null";
internal string SteamParentalPIN { get; private set; } = "0"; internal string SteamParentalPIN { get; private set; } = "0";
internal ulong SteamMasterID { get; private set; } = 76561198006963719; internal ulong SteamMasterID { get; private set; } = 0;
internal ulong SteamMasterClanID { get; private set; } = 0; internal ulong SteamMasterClanID { get; private set; } = 0;
internal bool ShutdownOnFarmingFinished { get; private set; } = false; internal bool ShutdownOnFarmingFinished { get; private set; } = false;
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint> { 368020 }; internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint> { 303700, 335590, 368020 };
internal bool Statistics { get; private set; } = true; internal bool Statistics { get; private set; } = true;
internal static int GetRunningBotsCount() { internal static int GetRunningBotsCount() {
@@ -78,7 +77,7 @@ namespace ArchiSteamFarm {
internal static async Task ShutdownAllBots() { internal static async Task ShutdownAllBots() {
List<Task> tasks = new List<Task>(); List<Task> tasks = new List<Task>();
lock (Bots) { lock (Bots) {
foreach (Bot bot in Bots) { foreach (Bot bot in Bots.Values) {
tasks.Add(Task.Run(async () => await bot.Shutdown().ConfigureAwait(false))); tasks.Add(Task.Run(async () => await bot.Shutdown().ConfigureAwait(false)));
} }
} }
@@ -86,19 +85,25 @@ namespace ArchiSteamFarm {
} }
internal Bot(string botName) { internal Bot(string botName) {
if (Bots.ContainsKey(botName)) {
return;
}
BotName = botName; BotName = botName;
ConfigFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".xml"); ConfigFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".xml");
SentryFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".bin"); SentryFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".bin");
ReadConfig(); if (!ReadConfig()) {
return;
}
if (!Enabled) { if (!Enabled) {
return; return;
} }
lock (Bots) { lock (Bots) {
Bots.Add(this); Bots.Add(BotName, this);
} }
// Initialize // Initialize
@@ -130,10 +135,15 @@ namespace ArchiSteamFarm {
Trading = new Trading(this); Trading = new Trading(this);
// Start // Start
Start(); var fireAndForget = Task.Run(async () => await Start().ConfigureAwait(false));
} }
private void ReadConfig() { private bool ReadConfig() {
if (!File.Exists(ConfigFile)) {
return false;
}
try {
using (XmlReader reader = XmlReader.Create(ConfigFile)) { using (XmlReader reader = XmlReader.Create(ConfigFile)) {
while (reader.Read()) { while (reader.Read()) {
if (reader.NodeType != XmlNodeType.Element) { if (reader.NodeType != XmlNodeType.Element) {
@@ -179,6 +189,7 @@ namespace ArchiSteamFarm {
ShutdownOnFarmingFinished = bool.Parse(value); ShutdownOnFarmingFinished = bool.Parse(value);
break; break;
case "Blacklist": case "Blacklist":
Blacklist.Clear();
foreach (string appID in value.Split(',')) { foreach (string appID in value.Split(',')) {
Blacklist.Add(uint.Parse(appID)); Blacklist.Add(uint.Parse(appID));
} }
@@ -192,17 +203,27 @@ namespace ArchiSteamFarm {
} }
} }
} }
} catch (XmlException e) {
Logging.LogGenericException(BotName, e);
Logging.LogGenericError(BotName, "Your config for this bot instance is invalid, it won't run!");
return false;
} }
internal void Start() { return true;
}
internal async Task Start() {
if (IsRunning) { if (IsRunning) {
return; return;
} }
SteamClient.Connect();
IsRunning = true; IsRunning = true;
Task.Run(() => HandleCallbacks()); Logging.LogGenericInfo(BotName, "Starting...");
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
SteamClient.Connect();
var fireAndForget = Task.Run(() => HandleCallbacks());
} }
internal async Task Stop() { internal async Task Stop() {
@@ -211,16 +232,27 @@ namespace ArchiSteamFarm {
} }
await CardsFarmer.StopFarming().ConfigureAwait(false); await CardsFarmer.StopFarming().ConfigureAwait(false);
SteamClient.Disconnect();
IsRunning = false; IsRunning = false;
SteamClient.Disconnect();
} }
internal async Task Shutdown() { private async Task<bool> Shutdown(string botNameToShutdown) {
await Stop().ConfigureAwait(false); Bot botToShutdown;
lock (Bots) { if (!Bots.TryGetValue(botNameToShutdown, out botToShutdown)) {
Bots.Remove(this); return false;
} }
Program.OnBotShutdown(this);
await botToShutdown.Stop().ConfigureAwait(false);
lock (Bots) {
Bots.Remove(botNameToShutdown);
}
Program.OnBotShutdown(botToShutdown);
return true;
}
internal async Task<bool> Shutdown() {
return await Shutdown(BotName).ConfigureAwait(false);
} }
internal async Task OnFarmingFinished() { internal async Task OnFarmingFinished() {
@@ -240,6 +272,63 @@ namespace ArchiSteamFarm {
} }
} }
private void SendMessageToUser(ulong steamID, string message) {
if (steamID == 0 || string.IsNullOrEmpty(message)) {
return;
}
SteamFriends.SendChatMessage(steamID, EChatEntryType.ChatMsg, message);
}
private void ResponseStatus(ulong steamID) {
if (steamID == 0) {
return;
}
SendMessageToUser(steamID, "Currently " + Bots.Count + " bots are running");
}
private void ResponseStart(ulong steamID, string botNameToStart) {
if (steamID == 0 || string.IsNullOrEmpty(botNameToStart)) {
return;
}
if (Bots.ContainsKey(botNameToStart)) {
SendMessageToUser(steamID, "That bot instance is already running!");
return;
}
new Bot(botNameToStart);
if (Bots.ContainsKey(botNameToStart)) {
SendMessageToUser(steamID, "Done!");
} else {
SendMessageToUser(steamID, "That bot instance failed to start, make sure that " + botNameToStart + ".xml config exists and bot is active!");
}
}
private async Task ResponseStop(ulong steamID, string botNameToShutdown) {
if (steamID == 0 || string.IsNullOrEmpty(botNameToShutdown)) {
return;
}
if (!Bots.ContainsKey(botNameToShutdown)) {
SendMessageToUser(steamID, "That bot instance is already inactive!");
return;
}
if (await Shutdown(botNameToShutdown).ConfigureAwait(false)) {
SendMessageToUser(steamID, "Done!");
} else {
SendMessageToUser(steamID, "That bot instance failed to shutdown!");
}
}
private void OnConnected(SteamClient.ConnectedCallback callback) { private void OnConnected(SteamClient.ConnectedCallback callback) {
if (callback == null) { if (callback == null) {
return; return;
@@ -275,17 +364,21 @@ namespace ArchiSteamFarm {
}); });
} }
private void OnDisconnected(SteamClient.DisconnectedCallback callback) { private async void OnDisconnected(SteamClient.DisconnectedCallback callback) {
if (callback == null) { if (callback == null) {
return; return;
} }
if (!IsRunning) {
return;
}
if (SteamClient == null) { if (SteamClient == null) {
return; return;
} }
Logging.LogGenericWarning(BotName, "Disconnected from Steam, reconnecting..."); Logging.LogGenericWarning(BotName, "Disconnected from Steam, reconnecting...");
Thread.Sleep(TimeSpan.FromMilliseconds(CallbackSleep)); await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
SteamClient.Connect(); SteamClient.Connect();
} }
@@ -336,16 +429,43 @@ namespace ArchiSteamFarm {
if (message.Length == 17 && message[5] == '-' && message[11] == '-') { if (message.Length == 17 && message[5] == '-' && message[11] == '-') {
ArchiHandler.RedeemKey(message); ArchiHandler.RedeemKey(message);
return;
} }
if (!message.StartsWith("!")) {
return;
}
if (!message.Contains(" ")) {
switch (message) { switch (message) {
case "!exit":
await ShutdownAllBots().ConfigureAwait(false);
break;
case "!farm": case "!farm":
await CardsFarmer.StartFarming().ConfigureAwait(false); await CardsFarmer.StartFarming().ConfigureAwait(false);
SendMessageToUser(steamID, "Done!");
break; break;
case "!exit": case "!restart":
await Program.Restart().ConfigureAwait(false);
break;
case "!status":
ResponseStatus(steamID);
break;
case "!stop":
await Shutdown().ConfigureAwait(false); await Shutdown().ConfigureAwait(false);
break; break;
} }
} else {
string[] args = message.Split(' ');
switch (args[0]) {
case "!start":
ResponseStart(steamID, args[1]);
break;
case "!stop":
await ResponseStop(steamID, args[1]).ConfigureAwait(false);
break;
}
}
} }
private void OnPersonaState(SteamFriends.PersonaStateCallback callback) { private void OnPersonaState(SteamFriends.PersonaStateCallback callback) {
@@ -418,8 +538,7 @@ namespace ArchiSteamFarm {
case EResult.TryAnotherCM: case EResult.TryAnotherCM:
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult + ", retrying..."); Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult + ", retrying...");
await Stop().ConfigureAwait(false); await Stop().ConfigureAwait(false);
Thread.Sleep(5000); await Start().ConfigureAwait(false);
Start();
break; break;
default: default:
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult); Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult);
@@ -484,7 +603,8 @@ namespace ArchiSteamFarm {
} }
var purchaseResult = callback.PurchaseResult; var purchaseResult = callback.PurchaseResult;
SteamFriends.SendChatMessage(SteamMasterID, EChatEntryType.ChatMsg, "Status: " + purchaseResult); var items = callback.Items;
SendMessageToUser(SteamMasterID, "Status: " + purchaseResult + " | Items: " + string.Join("", items));
if (purchaseResult == ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK) { if (purchaseResult == ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK) {
await CardsFarmer.StartFarming().ConfigureAwait(false); await CardsFarmer.StartFarming().ConfigureAwait(false);

View File

@@ -132,7 +132,7 @@ namespace ArchiSteamFarm {
FarmResetEvent.Set(); FarmResetEvent.Set();
while (NowFarming) { while (NowFarming) {
Logging.LogGenericInfo(Bot.BotName, "Waiting for reaction..."); Logging.LogGenericInfo(Bot.BotName, "Waiting for reaction...");
Thread.Sleep(1000); await Utilities.SleepAsync(1000).ConfigureAwait(false);
} }
FarmResetEvent.Reset(); FarmResetEvent.Reset();
Logging.LogGenericInfo(Bot.BotName, "Farming stopped!"); Logging.LogGenericInfo(Bot.BotName, "Farming stopped!");

View File

@@ -26,7 +26,8 @@ using System.Diagnostics;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal static class Debugging { internal static class Debugging {
internal static bool IsReleaseBuild { get; private set; } = true; internal static bool IsDebugBuild { get; private set; } = false;
internal static bool IsReleaseBuild { get { return !IsDebugBuild; } }
static Debugging() { static Debugging() {
MarkIfDebug(); MarkIfDebug();
@@ -34,7 +35,7 @@ namespace ArchiSteamFarm {
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void MarkIfDebug() { private static void MarkIfDebug() {
IsReleaseBuild = false; IsDebugBuild = true;
} }
} }
} }

View File

@@ -44,8 +44,11 @@ namespace ArchiSteamFarm {
private const string LatestGithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases/latest"; private const string LatestGithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases/latest";
internal static readonly object ConsoleLock = new object(); internal static readonly object ConsoleLock = new object();
private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1);
private static readonly ManualResetEvent ShutdownResetEvent = new ManualResetEvent(false); private static readonly ManualResetEvent ShutdownResetEvent = new ManualResetEvent(false);
private static readonly AssemblyName AssemblyName = Assembly.GetExecutingAssembly().GetName(); private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
private static readonly string ExecutablePath = Assembly.Location;
private static readonly AssemblyName AssemblyName = Assembly.GetName();
private static readonly string ExeName = AssemblyName.Name + ".exe"; private static readonly string ExeName = AssemblyName.Name + ".exe";
private static readonly string Version = AssemblyName.Version.ToString(); private static readonly string Version = AssemblyName.Version.ToString();
@@ -69,7 +72,7 @@ namespace ArchiSteamFarm {
if (localVersion.CompareTo(remoteVersion) < 0) { if (localVersion.CompareTo(remoteVersion) < 0) {
Logging.LogGenericNotice("", "New version is available!"); Logging.LogGenericNotice("", "New version is available!");
Logging.LogGenericNotice("", "Consider updating yourself!"); Logging.LogGenericNotice("", "Consider updating yourself!");
Thread.Sleep(5000); await Utilities.SleepAsync(5000).ConfigureAwait(false);
} else if (localVersion.CompareTo(remoteVersion) > 0) { } else if (localVersion.CompareTo(remoteVersion) > 0) {
Logging.LogGenericNotice("", "You're currently using pre-release version!"); Logging.LogGenericNotice("", "You're currently using pre-release version!");
Logging.LogGenericNotice("", "Be careful!"); Logging.LogGenericNotice("", "Be careful!");
@@ -81,6 +84,18 @@ namespace ArchiSteamFarm {
Environment.Exit(exitCode); Environment.Exit(exitCode);
} }
internal static async Task Restart() {
await Bot.ShutdownAllBots().ConfigureAwait(false);
System.Diagnostics.Process.Start(ExecutablePath);
Environment.Exit(0);
}
internal static async Task LimitSteamRequestsAsync() {
await SteamSemaphore.WaitAsync().ConfigureAwait(false);
await Utilities.SleepAsync(5 * 1000).ConfigureAwait(false); // We must add some delay to not get caught by Steam anty-DoS
SteamSemaphore.Release();
}
internal static string GetUserInput(string botLogin, EUserInputType userInputType) { internal static string GetUserInput(string botLogin, EUserInputType userInputType) {
string result; string result;
lock (ConsoleLock) { lock (ConsoleLock) {
@@ -104,13 +119,14 @@ namespace ArchiSteamFarm {
result = Console.ReadLine(); result = Console.ReadLine();
Console.Clear(); // For security purposes Console.Clear(); // For security purposes
} }
result = result.Trim(); // Get rid of all whitespace characters
return result; return result;
} }
internal static void OnBotShutdown(Bot bot) { internal static async void OnBotShutdown(Bot bot) {
if (Bot.GetRunningBotsCount() == 0) { if (Bot.GetRunningBotsCount() == 0) {
Logging.LogGenericInfo("Main", "No bots are running, exiting"); Logging.LogGenericInfo("Main", "No bots are running, exiting");
Thread.Sleep(5000); // This might be the only message user gets, consider giving him some time await Utilities.SleepAsync(5000).ConfigureAwait(false); // This might be the only message user gets, consider giving him some time
ShutdownResetEvent.Set(); ShutdownResetEvent.Set();
} }
} }
@@ -120,6 +136,16 @@ namespace ArchiSteamFarm {
Task.Run(async () => await CheckForUpdate().ConfigureAwait(false)).Wait(); Task.Run(async () => await CheckForUpdate().ConfigureAwait(false)).Wait();
// Allow loading configs from source tree if it's a debug build
if (Debugging.IsDebugBuild) {
for (var i = 0; i < 4; i++) {
Directory.SetCurrentDirectory("..");
if (Directory.Exists(ConfigDirectoryPath)) {
break;
}
}
}
if (!Directory.Exists(ConfigDirectoryPath)) { if (!Directory.Exists(ConfigDirectoryPath)) {
Logging.LogGenericError("Main", "Config directory doesn't exist!"); Logging.LogGenericError("Main", "Config directory doesn't exist!");
Console.ReadLine(); Console.ReadLine();

View File

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

View File

@@ -36,35 +36,34 @@ namespace ArchiSteamFarm {
Bot = bot; Bot = bot;
} }
internal void CheckTrades() { internal async void CheckTrades() {
if (ParsingTasks < 2) { if (ParsingTasks < 2) {
ParsingTasks++; ParsingTasks++;
Task.Run(() => ParseActiveTrades());
await Semaphore.WaitAsync().ConfigureAwait(false);
await ParseActiveTrades().ConfigureAwait(false);
Semaphore.Release();
ParsingTasks--;
} }
} }
private async Task ParseActiveTrades() { private async Task ParseActiveTrades() {
await Semaphore.WaitAsync().ConfigureAwait(false);
List<SteamTradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers(); List<SteamTradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
if (tradeOffers != null) { if (tradeOffers == null) {
return;
}
List<Task> tasks = new List<Task>(); List<Task> tasks = new List<Task>();
foreach (SteamTradeOffer tradeOffer in tradeOffers) { foreach (SteamTradeOffer tradeOffer in tradeOffers) {
if (tradeOffer.trade_offer_state == SteamTradeOffer.ETradeOfferState.Active) { if (tradeOffer.trade_offer_state == SteamTradeOffer.ETradeOfferState.Active) {
Task task = Task.Run(async () => { tasks.Add(Task.Run(async () => await ParseTrade(tradeOffer).ConfigureAwait(false)));
await ParseTrade(tradeOffer).ConfigureAwait(false);
});
tasks.Add(task);
} }
} }
await Task.WhenAll(tasks).ConfigureAwait(false); await Task.WhenAll(tasks).ConfigureAwait(false);
} }
ParsingTasks--;
Semaphore.Release();
}
private async Task ParseTrade(SteamTradeOffer tradeOffer) { private async Task ParseTrade(SteamTradeOffer tradeOffer) {
if (tradeOffer == null) { if (tradeOffer == null) {
return; return;

View File

@@ -35,6 +35,12 @@ using System.Threading.Tasks;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal static class Utilities { internal static class Utilities {
private static readonly Random Random = new Random();
internal static async Task SleepAsync(int miliseconds) {
await Task.Delay(miliseconds).ConfigureAwait(false);
}
internal static ulong OnlyNumbers(string inputString) { internal static ulong OnlyNumbers(string inputString) {
if (string.IsNullOrEmpty(inputString)) { if (string.IsNullOrEmpty(inputString)) {
return 0; return 0;

View File

@@ -1,8 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<configuration> <configuration>
<!-- Every bot should have it's own unique .xml configuration file, this is example on which you can base on --> <!-- Every bot should have it's own unique .xml configuration file, this is example on which you can base on -->
<!-- Notice, if you use special characters reserved for XML, you should escape them -->
<!-- Escape table: [& - &amp;] | [" - &quot;] | [' - &apos;] | [< - &lt;] | [> - &gt;] -->
<!-- So e.g. if your SteamPassword is "foo&" you should write value="foo&amp;" -->
<!-- Master switch to turn account on and off, set to "true" after you're done --> <!-- Master switch to turn account on and off, set to "true" after you're done -->
<!-- TIP: This bot instance won't run unless below switch is set to "true" --> <!-- TIP: This bot instance won't run unless below switch is set to "true" -->
<Enabled type="bool" value="false"/> <Enabled type="bool" value="false"/>
@@ -27,11 +31,16 @@
<!-- TIP: Most likely you don't want to change it. You can use "null" if you wish to enter PIN on every startup, 0 means there is no PIN --> <!-- TIP: Most likely you don't want to change it. You can use "null" if you wish to enter PIN on every startup, 0 means there is no PIN -->
<SteamParentalPIN type="string" value="0"/> <SteamParentalPIN type="string" value="0"/>
<!-- This is steamID of the bot-master - you, in steamID64 format --> <!-- This is steamID64 of the bot-master - you, [Example: 76561198006963719] -->
<!-- TIP: You can use "0", but bot won't accept steam cd-keys or trades from anybody" --> <!-- TIP: You can use "0", but bot won't accept steam cd-keys or trades from anybody" -->
<SteamMasterID type="ulong" value="76561198006963719"/> <SteamMasterID type="ulong" value="0"/>
<!-- This defines clan of the master, bot will join chatroom of that clan automatically after logging in --> <!-- This defines clan of the master, bot will join chatroom of that clan automatically after logging in if set -->
<!-- SteamMasterClanID could be found by visiting your group-page -->
<!-- [Example: http://steamcommunity.com/groups/hellokitty/] -->
<!-- Adding "memberslistxml/?xml=1" to the end of this url so it looks like -->
<!-- [Example: http://steamcommunity.com/groups/hellokitty/memberslistxml/?xml=1] -->
<!-- Finally replace the SteamMasterClanID value with groupID64 you got from your groups xml-file -->
<!-- TIP: Most likely you don't want to change it --> <!-- TIP: Most likely you don't want to change it -->
<SteamMasterClanID type="ulong" value="0"/> <SteamMasterClanID type="ulong" value="0"/>
@@ -43,7 +52,7 @@
<!-- Comma-separated list of IDs that should not be considered for farming --> <!-- Comma-separated list of IDs that should not be considered for farming -->
<!-- TIP: Most likely you don't want to change it --> <!-- TIP: Most likely you don't want to change it -->
<Blacklist type="HashSet(uint)" value="368020"/> <Blacklist type="HashSet(uint)" value="303700,335590,368020"/>
<!-- This enables statistics for me - bot will join Archi's SC Farm group and chat --> <!-- This enables statistics for me - bot will join Archi's SC Farm group and chat -->
<!-- Consider leaving it at "true", this way I can check how many running bots are active --> <!-- Consider leaving it at "true", this way I can check how many running bots are active -->

View File

@@ -1,13 +1,10 @@
# ArchiSteamFarm ArchiSteamFarm
===================
Big work-in-progress Big work-in-progress. This bot allows you to farm steam cards using multiple accounts simultaneously. Each account is defined by it's own XML config in `config` directory and you don´t need any steam-client running in the background.
Allows you to farm steam cards using multiple accounts simultaneously. **Current functions:**
Each account is defined by it's own XML config in "config" directory.
Current functions:
- Does not need Steam client running, or even a GUI. Fully based on SteamKit2 and reverse-engineered Steam protocol.
- Automatically farm steam cards using any number of active accounts - Automatically farm steam cards using any number of active accounts
- Automatically accept friend requests sent from master - Automatically accept friend requests sent from master
- Automatically accept all trades coming from master - Automatically accept all trades coming from master
@@ -15,12 +12,28 @@ Current functions:
- SteamGuard / 2-factor-authentication support - SteamGuard / 2-factor-authentication support
- Full Mono support, tested on Debian "9.0" Stretch (testing) - Full Mono support, tested on Debian "9.0" Stretch (testing)
TODO: **Current Commands:**
- Smart multi-games farming till every game reaches 2 hours, then one-by-one (similar to Idle Master) - Backend code is already here, just missing the logic and tests.
- Possible integration with SteamTradeMatcher, bot can detect dupes and trade them automatically. Again, backend code is already here, just missing actual implementation. - `!farm` Restarts the bot and starts card-farming (again)
- Automatic sending of steam trades to master, after game is fully farmed. - `!exit` Stops the bot
> You can use chat-commands in group-chat or private-chat with your bot.
> The MasterID has to be set for this specific bot / config-file.
**Supported / Tested Operating-Systems:**
- Windows 10 Enterprise Edition
- Debian 9.0 Stretch (Mono)
- Debian 8.1 Jessie (Mono)
**TODO**:
- Smart Multi-Game-Farming
- Possible integration with SteamTradeMatcher, bot can detect dupes and trade them automatically. Backend-code is already here, just missing actual implementation.
- Automatic sending of steamtrades to master(after game is fully farmed)
- Probably much more - Probably much more
This is big WIP, so feel free to send pull requests if you wish.
I'll release some releases later, when everything is tested and code cleaned up. > This is big WIP, so feel free to send pull requests if you wish. I'll
> release some releases later, when everything is tested and code
> cleaned up.