mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-17 06:50:29 +00:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44b3a518bd | ||
|
|
1fe60e7037 | ||
|
|
27ad98492f | ||
|
|
e3386c5b29 | ||
|
|
021ac470d3 | ||
|
|
b4fb0bf9a6 | ||
|
|
b63dd1035c | ||
|
|
f577e1a6cb | ||
|
|
34ffb975b6 | ||
|
|
5a3b132d5e | ||
|
|
ef29b6f33d | ||
|
|
e8335ac183 | ||
|
|
90339fb276 | ||
|
|
88759303dd | ||
|
|
befe01ca59 | ||
|
|
c9cc728da3 | ||
|
|
a81a59396d | ||
|
|
df3fcfb1df | ||
|
|
e097b33d89 | ||
|
|
db6e775b10 | ||
|
|
b53c41a0f9 | ||
|
|
fec4873843 | ||
|
|
1e2efe38f1 | ||
|
|
3e0dfac091 | ||
|
|
36d745aece | ||
|
|
7896e7924e | ||
|
|
5b3c730d51 | ||
|
|
f3aee0c34f | ||
|
|
47389f67b8 | ||
|
|
19e72c93c1 | ||
|
|
164240641b | ||
|
|
337c397505 | ||
|
|
137e8d1f63 | ||
|
|
fed3ac3404 | ||
|
|
3ba90e5d6d | ||
|
|
71227168cf | ||
|
|
2545e7bbf3 | ||
|
|
411f796fab | ||
|
|
fa6dbed593 |
@@ -24,6 +24,8 @@
|
||||
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ArchiHandler : ClientMsgHandler {
|
||||
@@ -33,21 +35,29 @@ namespace ArchiSteamFarm {
|
||||
Unknown = -1,
|
||||
OK = 0,
|
||||
AlreadyOwned = 9,
|
||||
RegionLockedKey = 13,
|
||||
InvalidKey = 14,
|
||||
DuplicatedKey = 15,
|
||||
BaseGameRequired = 24,
|
||||
OnCooldown = 53
|
||||
}
|
||||
|
||||
internal EResult Result { get; private set; }
|
||||
internal EPurchaseResult PurchaseResult { get; private set; }
|
||||
internal int ErrorCode { get; private set; }
|
||||
internal byte[] ReceiptInfo { get; private set; }
|
||||
internal KeyValue ReceiptInfo { get; private set; } = new KeyValue();
|
||||
internal Dictionary<uint, string> Items { get; private set; } = new Dictionary<uint, string>();
|
||||
|
||||
internal PurchaseResponseCallback(CMsgClientPurchaseResponse body) {
|
||||
Result = (EResult) body.eresult;
|
||||
ErrorCode = body.purchase_result_details;
|
||||
ReceiptInfo = body.purchase_receipt_info;
|
||||
PurchaseResult = (EPurchaseResult) ErrorCode;
|
||||
PurchaseResult = (EPurchaseResult) body.purchase_result_details;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,12 @@
|
||||
</DocumentationFile>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>cirno.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="HtmlAgilityPack, Version=1.4.9.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll</HintPath>
|
||||
@@ -83,6 +89,7 @@
|
||||
<Compile Include="Bot.cs" />
|
||||
<Compile Include="CardsFarmer.cs" />
|
||||
<Compile Include="CMsgClientClanInviteAction.cs" />
|
||||
<Compile Include="Debugging.cs" />
|
||||
<Compile Include="Logging.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
@@ -107,7 +114,25 @@
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="cirno.ico" />
|
||||
<Content Include="config\example.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' ">if $(ConfigurationName) == Release (
|
||||
mkdir "$(TargetDir)out" "$(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
|
||||
del "$(TargetDir)out\ASF.pdb"
|
||||
)</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -27,7 +27,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
@@ -35,88 +34,79 @@ namespace ArchiSteamFarm {
|
||||
internal class Bot {
|
||||
private const ushort CallbackSleep = 500; // In miliseconds
|
||||
|
||||
private readonly Dictionary<string, string> Config = new Dictionary<string, string>();
|
||||
private static readonly Dictionary<string, Bot> Bots = new Dictionary<string, Bot>();
|
||||
|
||||
internal readonly string BotName;
|
||||
private readonly string ConfigFile;
|
||||
private readonly string SentryFile;
|
||||
|
||||
private readonly CardsFarmer CardsFarmer;
|
||||
|
||||
internal ulong BotID { get; private set; }
|
||||
private bool IsRunning = false;
|
||||
private string AuthCode, TwoFactorAuth;
|
||||
|
||||
internal readonly string BotName;
|
||||
|
||||
internal ArchiHandler ArchiHandler { get; private set; }
|
||||
internal ArchiWebHandler ArchiWebHandler { get; private set; }
|
||||
internal CallbackManager CallbackManager { get; private set; }
|
||||
internal CardsFarmer CardsFarmer { get; private set; }
|
||||
internal SteamClient SteamClient { get; private set; }
|
||||
internal SteamFriends SteamFriends { get; private set; }
|
||||
internal SteamUser SteamUser { get; private set; }
|
||||
internal Trading Trading { get; private set; }
|
||||
|
||||
// Config variables
|
||||
internal bool Enabled { get { return bool.Parse(Config["Enabled"]); } }
|
||||
private string SteamLogin { get { return Config["SteamLogin"]; } }
|
||||
private string SteamPassword { get { return Config["SteamPassword"]; } }
|
||||
private string SteamNickname { get { return Config["SteamNickname"]; } }
|
||||
private string SteamApiKey { get { return Config["SteamApiKey"]; } }
|
||||
private string SteamParentalPIN { get { return Config["SteamParentalPIN"]; } }
|
||||
internal ulong SteamMasterID { get { return ulong.Parse(Config["SteamMasterID"]); } }
|
||||
private ulong SteamMasterClanID { get { return ulong.Parse(Config["SteamMasterClanID"]); } }
|
||||
internal HashSet<uint> Blacklist { get; } = new HashSet<uint>();
|
||||
internal bool Statistics { get { return bool.Parse(Config["Statistics"]); } }
|
||||
internal bool Enabled { get; private set; } = false;
|
||||
internal string SteamLogin { get; private set; } = "null";
|
||||
internal string SteamPassword { get; private set; } = "null";
|
||||
internal string SteamNickname { get; private set; } = "null";
|
||||
internal string SteamApiKey { get; private set; } = "null";
|
||||
internal string SteamParentalPIN { get; private set; } = "0";
|
||||
internal ulong SteamMasterID { get; private set; } = 0;
|
||||
internal ulong SteamMasterClanID { get; private set; } = 0;
|
||||
internal bool ShutdownOnFarmingFinished { get; private set; } = false;
|
||||
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint> { 303700, 335590, 368020 };
|
||||
internal bool Statistics { get; private set; } = true;
|
||||
|
||||
internal static int GetRunningBotsCount() {
|
||||
int result;
|
||||
lock (Bots) {
|
||||
result = Bots.Count;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static async Task ShutdownAllBots() {
|
||||
List<Task> tasks = new List<Task>();
|
||||
lock (Bots) {
|
||||
foreach (Bot bot in Bots.Values) {
|
||||
tasks.Add(Task.Run(async () => await bot.Shutdown().ConfigureAwait(false)));
|
||||
}
|
||||
}
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal Bot(string botName) {
|
||||
if (Bots.ContainsKey(botName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BotName = botName;
|
||||
CardsFarmer = new CardsFarmer(this);
|
||||
|
||||
ConfigFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".xml");
|
||||
SentryFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".bin");
|
||||
|
||||
ReadConfig();
|
||||
if (!ReadConfig()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
private void ReadConfig() {
|
||||
using (XmlReader reader = XmlReader.Create(ConfigFile)) {
|
||||
while (reader.Read()) {
|
||||
if (reader.NodeType != XmlNodeType.Element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string key = reader.Name;
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string value = reader.GetAttribute("value");
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Config.Add(key, value);
|
||||
|
||||
switch (key) {
|
||||
case "Blacklist":
|
||||
foreach (string appID in value.Split(',')) {
|
||||
Blacklist.Add(uint.Parse(appID));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void Start() {
|
||||
if (SteamClient != null) {
|
||||
return;
|
||||
lock (Bots) {
|
||||
Bots.Add(BotName, this);
|
||||
}
|
||||
|
||||
// Initialize
|
||||
SteamClient = new SteamClient();
|
||||
|
||||
ArchiHandler = new ArchiHandler();
|
||||
@@ -141,20 +131,134 @@ namespace ArchiSteamFarm {
|
||||
CallbackManager.Subscribe<ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse);
|
||||
|
||||
ArchiWebHandler = new ArchiWebHandler(this, SteamApiKey);
|
||||
CardsFarmer = new CardsFarmer(this);
|
||||
Trading = new Trading(this);
|
||||
|
||||
SteamClient.Connect();
|
||||
Task.Run(() => HandleCallbacks());
|
||||
// Start
|
||||
var fireAndForget = Task.Run(async () => await Start().ConfigureAwait(false));
|
||||
}
|
||||
|
||||
internal void Stop() {
|
||||
if (SteamClient == null) {
|
||||
private bool ReadConfig() {
|
||||
if (!File.Exists(ConfigFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
using (XmlReader reader = XmlReader.Create(ConfigFile)) {
|
||||
while (reader.Read()) {
|
||||
if (reader.NodeType != XmlNodeType.Element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string key = reader.Name;
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string value = reader.GetAttribute("value");
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "Enabled":
|
||||
Enabled = bool.Parse(value);
|
||||
break;
|
||||
case "SteamLogin":
|
||||
SteamLogin = value;
|
||||
break;
|
||||
case "SteamPassword":
|
||||
SteamPassword = value;
|
||||
break;
|
||||
case "SteamNickname":
|
||||
SteamNickname = value;
|
||||
break;
|
||||
case "SteamApiKey":
|
||||
SteamApiKey = value;
|
||||
break;
|
||||
case "SteamParentalPIN":
|
||||
SteamParentalPIN = value;
|
||||
break;
|
||||
case "SteamMasterID":
|
||||
SteamMasterID = ulong.Parse(value);
|
||||
break;
|
||||
case "SteamMasterClanID":
|
||||
SteamMasterClanID = ulong.Parse(value);
|
||||
break;
|
||||
case "ShutdownOnFarmingFinished":
|
||||
ShutdownOnFarmingFinished = bool.Parse(value);
|
||||
break;
|
||||
case "Blacklist":
|
||||
Blacklist.Clear();
|
||||
foreach (string appID in value.Split(',')) {
|
||||
Blacklist.Add(uint.Parse(appID));
|
||||
}
|
||||
break;
|
||||
case "Statistics":
|
||||
Statistics = bool.Parse(value);
|
||||
break;
|
||||
default:
|
||||
Logging.LogGenericWarning(BotName, "Unrecognized config value: " + key + "=" + value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (XmlException e) {
|
||||
Logging.LogGenericException(BotName, e);
|
||||
Logging.LogGenericError(BotName, "Your config for this bot instance is invalid, it won't run!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task Start() {
|
||||
if (IsRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
IsRunning = true;
|
||||
|
||||
Logging.LogGenericInfo(BotName, "Starting...");
|
||||
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
|
||||
SteamClient.Connect();
|
||||
|
||||
var fireAndForget = Task.Run(() => HandleCallbacks());
|
||||
}
|
||||
|
||||
internal async Task Stop() {
|
||||
if (!IsRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
await CardsFarmer.StopFarming().ConfigureAwait(false);
|
||||
IsRunning = false;
|
||||
SteamClient.Disconnect();
|
||||
SteamClient = null;
|
||||
CallbackManager = null;
|
||||
}
|
||||
|
||||
private async Task<bool> Shutdown(string botNameToShutdown) {
|
||||
Bot botToShutdown;
|
||||
if (!Bots.TryGetValue(botNameToShutdown, out botToShutdown)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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() {
|
||||
if (ShutdownOnFarmingFinished) {
|
||||
await Shutdown().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal void PlayGame(params ulong[] gameIDs) {
|
||||
@@ -163,11 +267,68 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private void HandleCallbacks() {
|
||||
TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep);
|
||||
while (CallbackManager != null) {
|
||||
while (IsRunning) {
|
||||
CallbackManager.RunWaitCallbacks(timeSpan);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (callback == null) {
|
||||
return;
|
||||
@@ -186,38 +347,38 @@ namespace ArchiSteamFarm {
|
||||
sentryHash = CryptoHelper.SHAHash(sentryFileContent);
|
||||
}
|
||||
|
||||
string steamLogin = SteamLogin;
|
||||
if (string.IsNullOrEmpty(steamLogin) || steamLogin.Equals("null")) {
|
||||
steamLogin = Program.GetUserInput(BotName, Program.EUserInputType.Login);
|
||||
Config["SteamLogin"] = steamLogin;
|
||||
if (SteamLogin.Equals("null")) {
|
||||
SteamLogin = Program.GetUserInput(BotName, Program.EUserInputType.Login);
|
||||
}
|
||||
|
||||
string steamPassword = SteamPassword;
|
||||
if (string.IsNullOrEmpty(steamPassword) || steamPassword.Equals("null")) {
|
||||
steamPassword = Program.GetUserInput(BotName, Program.EUserInputType.Password);
|
||||
Config["SteamPassword"] = steamPassword;
|
||||
if (SteamPassword.Equals("null")) {
|
||||
SteamPassword = Program.GetUserInput(BotName, Program.EUserInputType.Password);
|
||||
}
|
||||
|
||||
SteamUser.LogOn(new SteamUser.LogOnDetails {
|
||||
Username = steamLogin,
|
||||
Password = steamPassword,
|
||||
Username = SteamLogin,
|
||||
Password = SteamPassword,
|
||||
AuthCode = AuthCode,
|
||||
TwoFactorCode = TwoFactorAuth,
|
||||
SentryFileHash = sentryHash
|
||||
});
|
||||
}
|
||||
|
||||
private void OnDisconnected(SteamClient.DisconnectedCallback callback) {
|
||||
private async void OnDisconnected(SteamClient.DisconnectedCallback callback) {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SteamClient == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericWarning(BotName, "Disconnected from Steam, reconnecting...");
|
||||
Thread.Sleep(TimeSpan.FromMilliseconds(CallbackSleep));
|
||||
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
|
||||
SteamClient.Connect();
|
||||
}
|
||||
|
||||
@@ -247,7 +408,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFriendMsg(SteamFriends.FriendMsgCallback callback) {
|
||||
private async void OnFriendMsg(SteamFriends.FriendMsgCallback callback) {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
@@ -268,6 +429,42 @@ namespace ArchiSteamFarm {
|
||||
|
||||
if (message.Length == 17 && message[5] == '-' && message[11] == '-') {
|
||||
ArchiHandler.RedeemKey(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!message.StartsWith("!")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!message.Contains(" ")) {
|
||||
switch (message) {
|
||||
case "!exit":
|
||||
await ShutdownAllBots().ConfigureAwait(false);
|
||||
break;
|
||||
case "!farm":
|
||||
await CardsFarmer.StartFarming().ConfigureAwait(false);
|
||||
SendMessageToUser(steamID, "Done!");
|
||||
break;
|
||||
case "!restart":
|
||||
await Program.Restart().ConfigureAwait(false);
|
||||
break;
|
||||
case "!status":
|
||||
ResponseStatus(steamID);
|
||||
break;
|
||||
case "!stop":
|
||||
await Shutdown().ConfigureAwait(false);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,10 +501,6 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback.ClientSteamID != 0) {
|
||||
BotID = callback.ClientSteamID;
|
||||
}
|
||||
|
||||
EResult result = callback.Result;
|
||||
switch (result) {
|
||||
case EResult.AccountLogonDenied:
|
||||
@@ -319,18 +512,15 @@ namespace ArchiSteamFarm {
|
||||
case EResult.OK:
|
||||
Logging.LogGenericInfo(BotName, "Successfully logged on!");
|
||||
|
||||
string steamNickname = SteamNickname;
|
||||
if (!string.IsNullOrEmpty(steamNickname) && !steamNickname.Equals("null")) {
|
||||
SteamFriends.SetPersonaName(steamNickname);
|
||||
if (!SteamNickname.Equals("null")) {
|
||||
SteamFriends.SetPersonaName(SteamNickname);
|
||||
}
|
||||
|
||||
string steamParentalPIN = SteamParentalPIN;
|
||||
if (string.IsNullOrEmpty(steamParentalPIN) || steamParentalPIN.Equals("null")) {
|
||||
steamParentalPIN = Program.GetUserInput(BotName, Program.EUserInputType.SteamParentalPIN);
|
||||
Config["SteamParentalPIN"] = steamParentalPIN;
|
||||
if (SteamParentalPIN.Equals("null")) {
|
||||
SteamParentalPIN = Program.GetUserInput(BotName, Program.EUserInputType.SteamParentalPIN);
|
||||
}
|
||||
|
||||
await ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, callback.VanityURL, steamParentalPIN).ConfigureAwait(false);
|
||||
await ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, callback.VanityURL, SteamParentalPIN).ConfigureAwait(false);
|
||||
|
||||
ulong clanID = SteamMasterClanID;
|
||||
if (clanID != 0) {
|
||||
@@ -347,13 +537,12 @@ namespace ArchiSteamFarm {
|
||||
case EResult.Timeout:
|
||||
case EResult.TryAnotherCM:
|
||||
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult + ", retrying...");
|
||||
Stop();
|
||||
Thread.Sleep(5000);
|
||||
Start();
|
||||
await Stop().ConfigureAwait(false);
|
||||
await Start().ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult);
|
||||
Stop();
|
||||
await Shutdown().ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -414,7 +603,8 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
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) {
|
||||
await CardsFarmer.StartFarming().ConfigureAwait(false);
|
||||
|
||||
@@ -31,20 +31,33 @@ namespace ArchiSteamFarm {
|
||||
internal class CardsFarmer {
|
||||
private const byte StatusCheckSleep = 5; // In minutes, how long to wait before checking the appID again
|
||||
|
||||
private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false);
|
||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
||||
private readonly Bot Bot;
|
||||
private bool NowFarming;
|
||||
private readonly AutoResetEvent AutoResetEvent = new AutoResetEvent(false);
|
||||
|
||||
private volatile bool NowFarming = false;
|
||||
|
||||
internal CardsFarmer(Bot bot) {
|
||||
Bot = bot;
|
||||
}
|
||||
|
||||
internal async Task StartFarming() {
|
||||
await StopFarming().ConfigureAwait(false);
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (NowFarming) {
|
||||
Semaphore.Release();
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo(Bot.BotName, "Checking badges...");
|
||||
|
||||
// Find the number of badge pages
|
||||
HtmlDocument badgesDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false);
|
||||
if (badgesDocument == null) {
|
||||
Logging.LogGenericWarning(Bot.BotName, "Could not get badges information, farming is stopped!");
|
||||
Semaphore.Release();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,13 +87,13 @@ namespace ArchiSteamFarm {
|
||||
foreach (HtmlNode badgesPageNode in badgesPageNodes) {
|
||||
string steamLink = badgesPageNode.GetAttributeValue("href", null);
|
||||
if (steamLink == null) {
|
||||
Logging.LogGenericWarning(Bot.BotName, "Couldn't get steamLink for one of the games: " + badgesPageNode.OuterHtml);
|
||||
Logging.LogGenericError(Bot.BotName, "Couldn't get steamLink for one of the games: " + badgesPageNode.OuterHtml);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint appID = (uint) Utilities.OnlyNumbers(steamLink);
|
||||
if (appID == 0) {
|
||||
Logging.LogGenericWarning(Bot.BotName, "Couldn't get appID for one of the games: " + badgesPageNode.OuterHtml);
|
||||
Logging.LogGenericError(Bot.BotName, "Couldn't get appID for one of the games: " + badgesPageNode.OuterHtml);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -99,11 +112,31 @@ namespace ArchiSteamFarm {
|
||||
if (await Farm(appID).ConfigureAwait(false)) {
|
||||
appIDs.Remove(appID);
|
||||
} else {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo(Bot.BotName, "Farming finished!");
|
||||
await Bot.OnFarmingFinished().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task StopFarming() {
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (!NowFarming) {
|
||||
Semaphore.Release();
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo(Bot.BotName, "Sending signal to stop farming");
|
||||
FarmResetEvent.Set();
|
||||
while (NowFarming) {
|
||||
Logging.LogGenericInfo(Bot.BotName, "Waiting for reaction...");
|
||||
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
||||
}
|
||||
FarmResetEvent.Reset();
|
||||
Logging.LogGenericInfo(Bot.BotName, "Farming stopped!");
|
||||
Semaphore.Release();
|
||||
}
|
||||
|
||||
private async Task<bool?> ShouldFarm(ulong appID) {
|
||||
@@ -119,12 +152,6 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private async Task<bool> Farm(ulong appID) {
|
||||
if (NowFarming) {
|
||||
AutoResetEvent.Set();
|
||||
Thread.Sleep(1000);
|
||||
AutoResetEvent.Reset();
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||
while (keepFarming == null || keepFarming.Value) {
|
||||
@@ -132,17 +159,20 @@ namespace ArchiSteamFarm {
|
||||
NowFarming = true;
|
||||
Logging.LogGenericInfo(Bot.BotName, "Now farming: " + appID);
|
||||
Bot.PlayGame(appID);
|
||||
Semaphore.Release(); // We're farming, allow other tasks to shut us down
|
||||
} else {
|
||||
Logging.LogGenericInfo(Bot.BotName, "Still farming: " + appID);
|
||||
}
|
||||
if (AutoResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) {
|
||||
if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo(Bot.BotName, "Stopped farming: " + appID);
|
||||
Bot.PlayGame(0);
|
||||
NowFarming = false;
|
||||
Logging.LogGenericInfo(Bot.BotName, "Stopped farming: " + appID);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
41
ArchiSteamFarm/Debugging.cs
Normal file
41
ArchiSteamFarm/Debugging.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Ł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;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Debugging {
|
||||
internal static bool IsDebugBuild { get; private set; } = false;
|
||||
internal static bool IsReleaseBuild { get { return !IsDebugBuild; } }
|
||||
|
||||
static Debugging() {
|
||||
MarkIfDebug();
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private static void MarkIfDebug() {
|
||||
IsDebugBuild = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
@@ -44,9 +43,14 @@ namespace ArchiSteamFarm {
|
||||
internal const string ConfigDirectoryPath = "config";
|
||||
private const string LatestGithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases/latest";
|
||||
|
||||
private static readonly HashSet<Bot> Bots = new HashSet<Bot>();
|
||||
internal static readonly object ConsoleLock = new object();
|
||||
internal static string Version { get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); } }
|
||||
private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1);
|
||||
private static readonly ManualResetEvent ShutdownResetEvent = new ManualResetEvent(false);
|
||||
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 Version = AssemblyName.Version.ToString();
|
||||
|
||||
private static async Task CheckForUpdate() {
|
||||
JObject response = await Utilities.UrlToJObject(LatestGithubReleaseURL).ConfigureAwait(false);
|
||||
@@ -61,20 +65,37 @@ namespace ArchiSteamFarm {
|
||||
|
||||
string localVersion = Version;
|
||||
|
||||
Logging.LogGenericNotice("", "Local version: " + localVersion);
|
||||
Logging.LogGenericNotice("", "Remote version: " + remoteVersion);
|
||||
|
||||
int comparisonResult = localVersion.CompareTo(remoteVersion);
|
||||
if (localVersion.CompareTo(remoteVersion) < 0) {
|
||||
Logging.LogGenericNotice("", "New version is available!");
|
||||
Logging.LogGenericNotice("", "Local version: " + localVersion);
|
||||
Logging.LogGenericNotice("", "Remote version: " + remoteVersion);
|
||||
Logging.LogGenericNotice("", "Consider updating yourself!");
|
||||
Thread.Sleep(5000);
|
||||
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||
} else if (localVersion.CompareTo(remoteVersion) > 0) {
|
||||
Logging.LogGenericNotice("", "You're currently using pre-release version!");
|
||||
Logging.LogGenericNotice("", "Be careful!");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Exit(int exitCode = 0) {
|
||||
ShutdownAllBots();
|
||||
internal static async Task Exit(int exitCode = 0) {
|
||||
await Bot.ShutdownAllBots().ConfigureAwait(false);
|
||||
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) {
|
||||
string result;
|
||||
lock (ConsoleLock) {
|
||||
@@ -98,15 +119,15 @@ namespace ArchiSteamFarm {
|
||||
result = Console.ReadLine();
|
||||
Console.Clear(); // For security purposes
|
||||
}
|
||||
result = result.Trim(); // Get rid of all whitespace characters
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void ShutdownAllBots() {
|
||||
lock (Bots) {
|
||||
foreach (Bot bot in Bots) {
|
||||
bot.Stop();
|
||||
}
|
||||
Bots.Clear();
|
||||
internal static async void OnBotShutdown(Bot bot) {
|
||||
if (Bot.GetRunningBotsCount() == 0) {
|
||||
Logging.LogGenericInfo("Main", "No bots are running, exiting");
|
||||
await Utilities.SleepAsync(5000).ConfigureAwait(false); // This might be the only message user gets, consider giving him some time
|
||||
ShutdownResetEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,29 +136,34 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Task.Run(async () => await CheckForUpdate().ConfigureAwait(false)).Wait();
|
||||
|
||||
// Config directory may not be in the same directory as the .exe, check maximum of 3 levels lower
|
||||
for (var i = 0; i < 4 && !Directory.Exists(ConfigDirectoryPath); i++) {
|
||||
Directory.SetCurrentDirectory("..");
|
||||
// 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)) {
|
||||
Logging.LogGenericError("Main", "Config directory doesn't exist!");
|
||||
Console.ReadLine();
|
||||
Exit(1);
|
||||
Task.Run(async () => await Exit(1).ConfigureAwait(false)).Wait();
|
||||
}
|
||||
|
||||
lock (Bots) {
|
||||
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectoryPath, "*.xml")) {
|
||||
string botName = Path.GetFileNameWithoutExtension(configFile);
|
||||
Bot bot = new Bot(botName);
|
||||
Bots.Add(bot);
|
||||
if (!bot.Enabled) {
|
||||
Logging.LogGenericInfo(botName, "Not starting this instance because it's disabled in config file");
|
||||
}
|
||||
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectoryPath, "*.xml")) {
|
||||
string botName = Path.GetFileNameWithoutExtension(configFile);
|
||||
Bot bot = new Bot(botName);
|
||||
if (!bot.Enabled) {
|
||||
Logging.LogGenericInfo(botName, "Not starting this instance because it's disabled in config file");
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
// Check if we got any bots running
|
||||
OnBotShutdown(null);
|
||||
|
||||
ShutdownResetEvent.WaitOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.4.0.0")]
|
||||
[assembly: AssemblyFileVersion("0.4.0.0")]
|
||||
[assembly: AssemblyVersion("0.6.0.0")]
|
||||
[assembly: AssemblyFileVersion("0.6.0.0")]
|
||||
|
||||
@@ -55,9 +55,14 @@ namespace ArchiSteamFarm {
|
||||
internal bool from_real_time_trade { get; set; }
|
||||
|
||||
// Extra
|
||||
private ulong _OtherSteamID64 = 0;
|
||||
internal ulong OtherSteamID64 {
|
||||
get {
|
||||
return new SteamID((uint) accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64();
|
||||
if (_OtherSteamID64 == 0 && accountid_other != 0) {
|
||||
_OtherSteamID64 = new SteamID((uint) accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64();
|
||||
}
|
||||
|
||||
return _OtherSteamID64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,41 +28,40 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class Trading {
|
||||
private Bot Bot;
|
||||
private readonly Bot Bot;
|
||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
||||
private volatile byte ParsingTasks = 0;
|
||||
private SemaphoreSlim semaphore = new SemaphoreSlim(1);
|
||||
|
||||
internal Trading(Bot bot) {
|
||||
Bot = bot;
|
||||
}
|
||||
|
||||
internal void CheckTrades() {
|
||||
internal async void CheckTrades() {
|
||||
if (ParsingTasks < 2) {
|
||||
ParsingTasks++;
|
||||
Task.Run(() => ParseActiveTrades());
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
await ParseActiveTrades().ConfigureAwait(false);
|
||||
Semaphore.Release();
|
||||
|
||||
ParsingTasks--;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ParseActiveTrades() {
|
||||
await semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
List<SteamTradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
|
||||
if (tradeOffers != null) {
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (SteamTradeOffer tradeOffer in tradeOffers) {
|
||||
if (tradeOffer.trade_offer_state == SteamTradeOffer.ETradeOfferState.Active) {
|
||||
Task task = Task.Run(async () => {
|
||||
await ParseTrade(tradeOffer).ConfigureAwait(false);
|
||||
});
|
||||
tasks.Add(task);
|
||||
}
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
if (tradeOffers == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParsingTasks--;
|
||||
semaphore.Release();
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (SteamTradeOffer tradeOffer in tradeOffers) {
|
||||
if (tradeOffer.trade_offer_state == SteamTradeOffer.ETradeOfferState.Active) {
|
||||
tasks.Add(Task.Run(async () => await ParseTrade(tradeOffer).ConfigureAwait(false)));
|
||||
}
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ParseTrade(SteamTradeOffer tradeOffer) {
|
||||
@@ -75,11 +74,11 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
ulong steamID = tradeOffer.OtherSteamID64;
|
||||
ulong otherSteamID = tradeOffer.OtherSteamID64;
|
||||
bool success = false;
|
||||
bool tradeAccepted = false;
|
||||
|
||||
if (tradeOffer.items_to_give.Count == 0 || steamID == Bot.SteamMasterID) {
|
||||
if (tradeOffer.items_to_give.Count == 0 || otherSteamID == Bot.SteamMasterID) {
|
||||
tradeAccepted = true;
|
||||
success = await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
|
||||
} else {
|
||||
|
||||
@@ -35,6 +35,12 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
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) {
|
||||
if (string.IsNullOrEmpty(inputString)) {
|
||||
return 0;
|
||||
@@ -86,10 +92,6 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static async Task<HttpResponseMessage> UrlToHttpResponse(string websiteAddress) {
|
||||
return await UrlToHttpResponse(websiteAddress, null).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal static async Task<HtmlDocument> UrlToHtmlDocument(string websiteAddress, Dictionary<string, string> cookieVariables = null) {
|
||||
if (string.IsNullOrEmpty(websiteAddress)) {
|
||||
return null;
|
||||
|
||||
BIN
ArchiSteamFarm/cirno.ico
Normal file
BIN
ArchiSteamFarm/cirno.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
@@ -1,47 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<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 -->
|
||||
|
||||
<!-- 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" -->
|
||||
<Enabled type="bool" value="false"/>
|
||||
<!-- Notice, if you use special characters reserved for XML, you should escape them -->
|
||||
<!-- Escape table: [& - &] | [" - "] | [' - '] | [< - <] | [> - >] -->
|
||||
<!-- So e.g. if your SteamPassword is "foo&" you should write value="foo&" -->
|
||||
|
||||
<!-- This is your steam login, the one you use for logging in to steam -->
|
||||
<!-- TIP: You can use "null" if you wish to enter login on every startup -->
|
||||
<SteamLogin type="string" value="Foo"/>
|
||||
<!-- 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" -->
|
||||
<Enabled type="bool" value="false"/>
|
||||
|
||||
<!-- This is your steam password, the one you use for logging in to steam -->
|
||||
<!-- TIP: You can use "null" if you wish to enter password on every startup -->
|
||||
<SteamPassword type="string" value="Bar"/>
|
||||
<!-- This is your steam login, the one you use for logging in to steam -->
|
||||
<!-- TIP: You can use "null" if you wish to enter login on every startup -->
|
||||
<SteamLogin type="string" value="null"/>
|
||||
|
||||
<!-- This is steam nickname, the one you want to use for bot. Can be anything up to 32 characters -->
|
||||
<!-- TIP: You can use "null" if you wish to preserve your actual nickname -->
|
||||
<SteamNickname type="string" value="null"/>
|
||||
<!-- This is your steam password, the one you use for logging in to steam -->
|
||||
<!-- TIP: You can use "null" if you wish to enter password on every startup -->
|
||||
<SteamPassword type="string" value="null"/>
|
||||
|
||||
<!-- This is your bot's API key, get one at https://steamcommunity.com/dev/apikey while logged in as bot, domain doesn't matter -->
|
||||
<!-- TIP: You can use "null", but it will disable all API-based functionalities such as trading -->
|
||||
<SteamApiKey type="string" value="null"/>
|
||||
<!-- This is steam nickname, the one you want to use for bot. Can be anything up to 32 characters -->
|
||||
<!-- TIP: You can use "null" if you wish to preserve your actual nickname -->
|
||||
<SteamNickname type="string" value="null"/>
|
||||
|
||||
<!-- This is your parental PIN if you use steam parental functionality -->
|
||||
<!-- 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"/>
|
||||
<!-- This is your bot's API key, get one at https://steamcommunity.com/dev/apikey while logged in as bot, domain doesn't matter -->
|
||||
<!-- TIP: You can use "null", but it will disable all API-based functionalities such as trading -->
|
||||
<SteamApiKey type="string" value="null"/>
|
||||
|
||||
<!-- This is steamID of the bot-master - you, in steamID64 format -->
|
||||
<!-- TIP: You can use "0", but bot won't accept steam cd-keys or trades from anybody" -->
|
||||
<SteamMasterID type="ulong" value="76561198006963719"/>
|
||||
<!-- This is your parental PIN if you use steam parental functionality -->
|
||||
<!-- 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"/>
|
||||
|
||||
<!-- This defines clan of the master, bot will join chatroom of that clan automatically after logging in -->
|
||||
<!-- TIP: Most likely you don't want to change it -->
|
||||
<SteamMasterClanID type="ulong" value="0"/>
|
||||
<!-- 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" -->
|
||||
<SteamMasterID type="ulong" value="0"/>
|
||||
|
||||
<!-- Comma-separated list of IDs that should not be considered for farming -->
|
||||
<!-- TIP: Most likely you don't want to change it -->
|
||||
<Blacklist type="HashSet(uint)" value="368020"/>
|
||||
<!-- 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 -->
|
||||
<SteamMasterClanID type="ulong" value="0"/>
|
||||
|
||||
<!-- 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 -->
|
||||
<!-- TIP: Group link is http://steamcommunity.com/groups/ascfarm -->
|
||||
<Statistics type="bool" value="true"/>
|
||||
<!-- This switch defines if bot should disconnect once farming is finished -->
|
||||
<!-- When no bots are active, ASF will shutdown as well -->
|
||||
<!-- Some people may want to keep their bots 24/7, other disconnect them after job is done -->
|
||||
<!-- Choose yourself what you prefer -->
|
||||
<ShutdownOnFarmingFinished type="bool" value="false"/>
|
||||
|
||||
</configuration>
|
||||
<!-- Comma-separated list of IDs that should not be considered for farming -->
|
||||
<!-- TIP: Most likely you don't want to change it -->
|
||||
<Blacklist type="HashSet(uint)" value="303700,335590,368020"/>
|
||||
|
||||
<!-- 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 -->
|
||||
<!-- TIP: Group link is http://steamcommunity.com/groups/ascfarm -->
|
||||
<Statistics type="bool" value="true"/>
|
||||
|
||||
</configuration>
|
||||
|
||||
49
README.md
49
README.md
@@ -1,26 +1,39 @@
|
||||
# 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.
|
||||
- Automatically farm steam cards using any number of active accounts
|
||||
- Automatically accept friend requests sent from master
|
||||
- Automatically accept all trades coming from master
|
||||
- Automatically accept all steam cd-keys sent via chat from master
|
||||
- SteamGuard / 2-factor-authentication support
|
||||
- Full Mono support, tested on Debian "9.0" Stretch (testing)
|
||||
|
||||
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 accept friend requests sent from master
|
||||
- Automatically accept all trades coming from master
|
||||
- Automatically accept all steam cd-keys sent via chat from master
|
||||
- SteamGuard / 2-factor-authentication support
|
||||
- Full Mono support, tested on Debian "9.0" Stretch (testing)
|
||||
**Current Commands:**
|
||||
|
||||
TODO:
|
||||
- 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.
|
||||
- Automatic sending of steam trades to master, after game is fully farmed.
|
||||
- `!farm` Restarts the bot and starts card-farming (again)
|
||||
- `!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
|
||||
|
||||
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.
|
||||
|
||||
BIN
tools/ILMerge.exe
Normal file
BIN
tools/ILMerge.exe
Normal file
Binary file not shown.
Reference in New Issue
Block a user