mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-20 08:18:37 +00:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b60b864aca | ||
|
|
4c64141462 | ||
|
|
4b50596709 | ||
|
|
622f060575 | ||
|
|
20038e8c86 | ||
|
|
b8faca2517 | ||
|
|
6f93139a18 | ||
|
|
50b5c7b87f | ||
|
|
b60448ef4c | ||
|
|
fad08a1fa9 | ||
|
|
a180c100c6 | ||
|
|
9d97ca16e8 | ||
|
|
e833415718 | ||
|
|
6bb296a674 | ||
|
|
f1e5874868 | ||
|
|
38e2088d09 | ||
|
|
8447e07aa0 | ||
|
|
fbe4e4bc6d | ||
|
|
f1213607ce | ||
|
|
de832c530b | ||
|
|
0c872b17e2 | ||
|
|
5fcbb85b4c | ||
|
|
3cdc93d373 | ||
|
|
36e99d9139 | ||
|
|
7bee2d468b | ||
|
|
8630cc40c8 | ||
|
|
3683195a0e | ||
|
|
7f5b946645 | ||
|
|
fdb194fe67 | ||
|
|
9063b9206b | ||
|
|
8d300894e5 | ||
|
|
3a0d3c444e | ||
|
|
8118fe0690 | ||
|
|
344c2ad23d | ||
|
|
d1a6613541 | ||
|
|
3dc88c65aa | ||
|
|
46384829c9 | ||
|
|
415ee8cc57 | ||
|
|
351d45e049 | ||
|
|
f81bbc60c5 | ||
|
|
eb6e93a172 | ||
|
|
e17c3ecf2a | ||
|
|
048b0fb538 | ||
|
|
ac7ecb6bb4 | ||
|
|
fcaf038dac | ||
|
|
f3da5d6afc | ||
|
|
84f33fcef4 | ||
|
|
22f0d423a3 | ||
|
|
f1d7609796 | ||
|
|
77386ecae5 | ||
|
|
4e86d21ef8 | ||
|
|
044fc87691 | ||
|
|
79fad62a4d | ||
|
|
6622d3b147 | ||
|
|
6b6d5429ad | ||
|
|
d59d0a8415 | ||
|
|
e3100d3938 | ||
|
|
42c020e552 | ||
|
|
554273833b | ||
|
|
093a29df62 | ||
|
|
31aa6b2e4a | ||
|
|
9f7ecdf054 | ||
|
|
7c4c74bf84 | ||
|
|
b8f03abd8b | ||
|
|
47f846540b | ||
|
|
bc14713079 | ||
|
|
770a8fee66 | ||
|
|
1df9af08e6 | ||
|
|
27464f6120 | ||
|
|
dfd45c6e25 | ||
|
|
adc1759cee | ||
|
|
88369ec71a | ||
|
|
a5d8ae53dd | ||
|
|
cd7b65868a | ||
|
|
7575704a01 | ||
|
|
b6ce8f435c | ||
|
|
565acca9fb | ||
|
|
610954ba73 | ||
|
|
74a748b03f | ||
|
|
585a075ec9 | ||
|
|
891d40afe1 | ||
|
|
387f0dd1c7 | ||
|
|
8b4d3c219c | ||
|
|
365877ec89 | ||
|
|
f03a43d573 | ||
|
|
d15a9cbfca | ||
|
|
acfad624fb |
@@ -1,4 +1,3 @@
|
|||||||
sudo: false
|
|
||||||
language: csharp
|
language: csharp
|
||||||
solution: ArchiSteamFarm.sln
|
solution: ArchiSteamFarm.sln
|
||||||
|
|
||||||
@@ -8,3 +7,6 @@ git:
|
|||||||
mono:
|
mono:
|
||||||
- weekly
|
- weekly
|
||||||
- latest
|
- latest
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ using SteamKit2;
|
|||||||
using SteamKit2.Internal;
|
using SteamKit2.Internal;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -36,7 +38,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
internal ArchiHandler(Bot bot) {
|
internal ArchiHandler(Bot bot) {
|
||||||
if (bot == null) {
|
if (bot == null) {
|
||||||
throw new ArgumentNullException("bot");
|
throw new ArgumentNullException(nameof(bot));
|
||||||
}
|
}
|
||||||
|
|
||||||
Bot = bot;
|
Bot = bot;
|
||||||
@@ -52,69 +54,60 @@ namespace ArchiSteamFarm {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
internal sealed class NotificationsCallback : CallbackMsg {
|
internal sealed class NotificationsCallback : CallbackMsg {
|
||||||
internal sealed class Notification {
|
internal enum ENotification : byte {
|
||||||
internal enum ENotificationType : uint {
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
Trading = 1,
|
Trading = 1,
|
||||||
// Only custom below, different than ones available as user_notification_type
|
// Only custom below, different than ones available as user_notification_type
|
||||||
Items = 514
|
Items = 255
|
||||||
}
|
|
||||||
|
|
||||||
internal readonly ENotificationType NotificationType;
|
|
||||||
|
|
||||||
internal Notification(ENotificationType notificationType) {
|
|
||||||
NotificationType = notificationType;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly List<Notification> Notifications;
|
internal readonly HashSet<ENotification> Notifications;
|
||||||
|
|
||||||
internal NotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
|
internal NotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
|
||||||
JobID = jobID;
|
if ((jobID == null) || (msg == null)) {
|
||||||
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
||||||
if (msg == null || msg.notifications == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Notifications = new List<Notification>(msg.notifications.Count);
|
JobID = jobID;
|
||||||
foreach (var notification in msg.notifications) {
|
|
||||||
Notifications.Add(new Notification((Notification.ENotificationType) notification.user_notification_type));
|
Notifications = new HashSet<ENotification>();
|
||||||
|
foreach (CMsgClientUserNotifications.Notification notification in msg.notifications) {
|
||||||
|
Notifications.Add((ENotification) notification.user_notification_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal NotificationsCallback(JobID jobID, CMsgClientItemAnnouncements msg) {
|
internal NotificationsCallback(JobID jobID, CMsgClientItemAnnouncements msg) {
|
||||||
JobID = jobID;
|
if ((jobID == null) || (msg == null)) {
|
||||||
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
||||||
if (msg == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JobID = jobID;
|
||||||
|
|
||||||
if (msg.count_new_items > 0) {
|
if (msg.count_new_items > 0) {
|
||||||
Notifications = new List<Notification>(1) {
|
Notifications = new HashSet<ENotification> {
|
||||||
new Notification(Notification.ENotificationType.Items)
|
ENotification.Items
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class OfflineMessageCallback : CallbackMsg {
|
internal sealed class OfflineMessageCallback : CallbackMsg {
|
||||||
internal readonly uint OfflineMessages;
|
internal readonly uint OfflineMessagesCount;
|
||||||
internal readonly List<uint> Users;
|
|
||||||
|
|
||||||
internal OfflineMessageCallback(JobID jobID, CMsgClientOfflineMessageNotification msg) {
|
internal OfflineMessageCallback(JobID jobID, CMsgClientOfflineMessageNotification msg) {
|
||||||
JobID = jobID;
|
if ((jobID == null) || (msg == null)) {
|
||||||
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
||||||
if (msg == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OfflineMessages = msg.offline_messages;
|
JobID = jobID;
|
||||||
Users = msg.friends_with_offline_messages;
|
OfflineMessagesCount = msg.offline_messages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class PurchaseResponseCallback : CallbackMsg {
|
internal sealed class PurchaseResponseCallback : CallbackMsg {
|
||||||
internal enum EPurchaseResult : int {
|
internal enum EPurchaseResult : sbyte {
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
Unknown = -1,
|
Unknown = -1,
|
||||||
OK = 0,
|
OK = 0,
|
||||||
AlreadyOwned = 9,
|
AlreadyOwned = 9,
|
||||||
@@ -125,39 +118,35 @@ namespace ArchiSteamFarm {
|
|||||||
OnCooldown = 53
|
OnCooldown = 53
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly EResult Result;
|
|
||||||
internal readonly EPurchaseResult PurchaseResult;
|
internal readonly EPurchaseResult PurchaseResult;
|
||||||
internal readonly KeyValue ReceiptInfo;
|
|
||||||
internal readonly Dictionary<uint, string> Items;
|
internal readonly Dictionary<uint, string> Items;
|
||||||
|
|
||||||
internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
|
internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
|
||||||
JobID = jobID;
|
if ((jobID == null) || (msg == null)) {
|
||||||
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
||||||
if (msg == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result = (EResult) msg.eresult;
|
JobID = jobID;
|
||||||
PurchaseResult = (EPurchaseResult) msg.purchase_result_details;
|
PurchaseResult = (EPurchaseResult) msg.purchase_result_details;
|
||||||
|
|
||||||
if (msg.purchase_receipt_info == null) {
|
if (msg.purchase_receipt_info == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReceiptInfo = new KeyValue();
|
KeyValue receiptInfo = new KeyValue();
|
||||||
using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) {
|
using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) {
|
||||||
if (!ReceiptInfo.TryReadAsBinary(ms)) {
|
if (!receiptInfo.TryReadAsBinary(ms)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<KeyValue> lineItems = ReceiptInfo["lineitems"].Children;
|
List<KeyValue> lineItems = receiptInfo["lineitems"].Children;
|
||||||
Items = new Dictionary<uint, string>(lineItems.Count);
|
Items = new Dictionary<uint, string>(lineItems.Count);
|
||||||
|
|
||||||
foreach (KeyValue lineItem in lineItems) {
|
foreach (KeyValue lineItem in lineItems) {
|
||||||
uint appID = (uint) lineItem["PackageID"].AsUnsignedLong();
|
uint appID = (uint) lineItem["PackageID"].AsUnsignedLong();
|
||||||
string gameName = lineItem["ItemDescription"].AsString();
|
string gameName = lineItem["ItemDescription"].Value;
|
||||||
gameName = WebUtility.UrlDecode(gameName); // Apparently steam expects client to decode sent HTML
|
gameName = WebUtility.HtmlDecode(gameName); // Apparently steam expects client to decode sent HTML
|
||||||
Items.Add(appID, gameName);
|
Items[appID] = gameName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,83 +161,42 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
internal void AcceptClanInvite(ulong clanID) {
|
|
||||||
if (clanID == 0 || !Client.IsConnected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = new ClientMsg<CMsgClientClanInviteAction>((int) EMsg.ClientAcknowledgeClanInvite);
|
|
||||||
request.Body.GroupID = clanID;
|
|
||||||
request.Body.AcceptInvite = true;
|
|
||||||
|
|
||||||
Client.Send(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void DeclineClanInvite(ulong clanID) {
|
|
||||||
if (clanID == 0 || !Client.IsConnected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = new ClientMsg<CMsgClientClanInviteAction>((int) EMsg.ClientAcknowledgeClanInvite);
|
|
||||||
request.Body.GroupID = clanID;
|
|
||||||
request.Body.AcceptInvite = false;
|
|
||||||
|
|
||||||
Client.Send(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void PlayGame(string gameName) {
|
internal void PlayGame(string gameName) {
|
||||||
if (!Client.IsConnected) {
|
if (!Client.IsConnected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
|
ClientMsgProtobuf<CMsgClientGamesPlayed> request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
|
||||||
|
|
||||||
var gamePlayed = new CMsgClientGamesPlayed.GamePlayed();
|
|
||||||
if (!string.IsNullOrEmpty(gameName)) {
|
if (!string.IsNullOrEmpty(gameName)) {
|
||||||
gamePlayed.game_id = new GameID() {
|
|
||||||
AppType = GameID.GameType.Shortcut,
|
|
||||||
ModID = uint.MaxValue
|
|
||||||
};
|
|
||||||
gamePlayed.game_extra_info = gameName;
|
|
||||||
}
|
|
||||||
|
|
||||||
request.Body.games_played.Add(gamePlayed);
|
|
||||||
|
|
||||||
Client.Send(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void PlayGames(params uint[] gameIDs) {
|
|
||||||
if (!Client.IsConnected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
|
|
||||||
foreach (uint gameID in gameIDs) {
|
|
||||||
if (gameID == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
||||||
game_id = new GameID(gameID),
|
game_extra_info = gameName,
|
||||||
|
game_id = new GameID {
|
||||||
|
AppType = GameID.GameType.Shortcut,
|
||||||
|
ModID = uint.MaxValue
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.Send(request);
|
Client.Send(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void PlayGames(ICollection<uint> gameIDs) {
|
internal void PlayGames(uint gameID) {
|
||||||
if (gameIDs == null || !Client.IsConnected) {
|
if (!Client.IsConnected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
|
PlayGames(new HashSet<uint> { gameID });
|
||||||
foreach (uint gameID in gameIDs) {
|
}
|
||||||
if (gameID == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
internal void PlayGames(ICollection<uint> gameIDs) {
|
||||||
|
if ((gameIDs == null) || !Client.IsConnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMsgProtobuf<CMsgClientGamesPlayed> request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
|
||||||
|
foreach (uint gameID in gameIDs.Where(gameID => gameID != 0)) {
|
||||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
||||||
game_id = new GameID(gameID),
|
game_id = new GameID(gameID)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +208,7 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey) {
|
ClientMsgProtobuf<CMsgClientRegisterKey> request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey) {
|
||||||
SourceJobID = Client.GetNextJobID()
|
SourceJobID = Client.GetNextJobID()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -284,8 +232,11 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
SteamID steamID = new SteamID(details.AccountID, details.AccountInstance, Client.ConnectedUniverse, EAccountType.Individual);
|
SteamID steamID = new SteamID(details.AccountID, details.AccountInstance, Client.ConnectedUniverse, EAccountType.Individual);
|
||||||
|
|
||||||
var logon = new ClientMsgProtobuf<CMsgClientLogon>(EMsg.ClientLogon);
|
ClientMsgProtobuf<CMsgClientLogon> logon = new ClientMsgProtobuf<CMsgClientLogon>(EMsg.ClientLogon);
|
||||||
logon.Body.obfustucated_private_ip = details.LoginID.Value;
|
if (details.LoginID != null) {
|
||||||
|
logon.Body.obfustucated_private_ip = details.LoginID.Value;
|
||||||
|
}
|
||||||
|
|
||||||
logon.ProtoHeader.client_sessionid = 0;
|
logon.ProtoHeader.client_sessionid = 0;
|
||||||
logon.ProtoHeader.steamid = steamID.ConvertToUInt64();
|
logon.ProtoHeader.steamid = steamID.ConvertToUInt64();
|
||||||
logon.Body.account_name = details.Username;
|
logon.Body.account_name = details.Username;
|
||||||
@@ -341,7 +292,7 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = new ClientMsgProtobuf<CMsgClientOfflineMessageNotification>(packetMsg);
|
ClientMsgProtobuf<CMsgClientOfflineMessageNotification> response = new ClientMsgProtobuf<CMsgClientOfflineMessageNotification>(packetMsg);
|
||||||
Client.PostCallback(new OfflineMessageCallback(packetMsg.TargetJobID, response.Body));
|
Client.PostCallback(new OfflineMessageCallback(packetMsg.TargetJobID, response.Body));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +301,7 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = new ClientMsgProtobuf<CMsgClientItemAnnouncements>(packetMsg);
|
ClientMsgProtobuf<CMsgClientItemAnnouncements> response = new ClientMsgProtobuf<CMsgClientItemAnnouncements>(packetMsg);
|
||||||
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
|
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +310,7 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = new ClientMsgProtobuf<CMsgClientPurchaseResponse>(packetMsg);
|
ClientMsgProtobuf<CMsgClientPurchaseResponse> response = new ClientMsgProtobuf<CMsgClientPurchaseResponse>(packetMsg);
|
||||||
Client.PostCallback(new PurchaseResponseCallback(packetMsg.TargetJobID, response.Body));
|
Client.PostCallback(new PurchaseResponseCallback(packetMsg.TargetJobID, response.Body));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,7 +319,7 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
|
ClientMsgProtobuf<CMsgClientUserNotifications> response = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
|
||||||
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
|
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,10 +99,11 @@
|
|||||||
<Compile Include="ArchiWebHandler.cs" />
|
<Compile Include="ArchiWebHandler.cs" />
|
||||||
<Compile Include="Bot.cs" />
|
<Compile Include="Bot.cs" />
|
||||||
<Compile Include="BotConfig.cs" />
|
<Compile Include="BotConfig.cs" />
|
||||||
|
<Compile Include="ConcurrentEnumerator.cs" />
|
||||||
|
<Compile Include="ConcurrentHashSet.cs" />
|
||||||
<Compile Include="GlobalDatabase.cs" />
|
<Compile Include="GlobalDatabase.cs" />
|
||||||
<Compile Include="BotDatabase.cs" />
|
<Compile Include="BotDatabase.cs" />
|
||||||
<Compile Include="CardsFarmer.cs" />
|
<Compile Include="CardsFarmer.cs" />
|
||||||
<Compile Include="CMsgs\CMsgClientClanInviteAction.cs" />
|
|
||||||
<Compile Include="Debugging.cs" />
|
<Compile Include="Debugging.cs" />
|
||||||
<Compile Include="GlobalConfig.cs" />
|
<Compile Include="GlobalConfig.cs" />
|
||||||
<Compile Include="JSON\GitHub.cs" />
|
<Compile Include="JSON\GitHub.cs" />
|
||||||
|
|||||||
@@ -28,19 +28,18 @@ using SteamKit2;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using ArchiSteamFarm.JSON;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal sealed class ArchiWebHandler {
|
internal sealed class ArchiWebHandler {
|
||||||
private const string SteamCommunity = "steamcommunity.com";
|
private const string SteamCommunityHost = "steamcommunity.com";
|
||||||
private const byte MinSessionTTL = 15; // Assume session is valid for at least that amount of seconds
|
private const byte MinSessionTTL = 15; // Assume session is valid for at least that amount of seconds
|
||||||
|
|
||||||
private static string SteamCommunityURL = "https://" + SteamCommunity;
|
private static string SteamCommunityURL = "https://" + SteamCommunityHost;
|
||||||
|
|
||||||
private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000;
|
private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000;
|
||||||
|
|
||||||
private readonly Bot Bot;
|
private readonly Bot Bot;
|
||||||
@@ -51,12 +50,57 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
internal static void Init() {
|
internal static void Init() {
|
||||||
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
||||||
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunity;
|
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunityHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint GetAppIDFromMarketHashName(string hashName) {
|
||||||
|
if (string.IsNullOrEmpty(hashName)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = hashName.IndexOf('-');
|
||||||
|
if (index < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint appID;
|
||||||
|
return !uint.TryParse(hashName.Substring(0, index), out appID) ? 0 : appID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Steam.Item.EType GetItemType(string name) {
|
||||||
|
if (string.IsNullOrEmpty(name)) {
|
||||||
|
return Steam.Item.EType.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case "Booster Pack":
|
||||||
|
return Steam.Item.EType.BoosterPack;
|
||||||
|
case "Coupon":
|
||||||
|
return Steam.Item.EType.Coupon;
|
||||||
|
case "Gift":
|
||||||
|
return Steam.Item.EType.Gift;
|
||||||
|
case "Steam Gems":
|
||||||
|
return Steam.Item.EType.SteamGems;
|
||||||
|
default:
|
||||||
|
if (name.EndsWith("Emoticon", StringComparison.Ordinal)) {
|
||||||
|
return Steam.Item.EType.Emoticon;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.EndsWith("Foil Trading Card", StringComparison.Ordinal)) {
|
||||||
|
return Steam.Item.EType.FoilTradingCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.EndsWith("Profile Background", StringComparison.Ordinal)) {
|
||||||
|
return Steam.Item.EType.ProfileBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name.EndsWith("Trading Card", StringComparison.Ordinal) ? Steam.Item.EType.TradingCard : Steam.Item.EType.Unknown;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ArchiWebHandler(Bot bot) {
|
internal ArchiWebHandler(Bot bot) {
|
||||||
if (bot == null) {
|
if (bot == null) {
|
||||||
throw new ArgumentNullException("bot");
|
throw new ArgumentNullException(nameof(bot));
|
||||||
}
|
}
|
||||||
|
|
||||||
Bot = bot;
|
Bot = bot;
|
||||||
@@ -65,11 +109,14 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
|
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
|
||||||
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) {
|
if ((steamClient == null) || string.IsNullOrEmpty(webAPIUserNonce)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong steamID = steamClient.SteamID;
|
ulong steamID = steamClient.SteamID;
|
||||||
|
if (steamID == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
|
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
|
||||||
|
|
||||||
@@ -116,13 +163,13 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
Logging.LogGenericInfo("Success!", Bot.BotName);
|
Logging.LogGenericInfo("Success!", Bot.BotName);
|
||||||
|
|
||||||
WebBrowser.CookieContainer.Add(new Cookie("sessionid", sessionID, "/", "." + SteamCommunity));
|
WebBrowser.CookieContainer.Add(new Cookie("sessionid", sessionID, "/", "." + SteamCommunityHost));
|
||||||
|
|
||||||
string steamLogin = authResult["token"].Value;
|
string steamLogin = authResult["token"].Value;
|
||||||
WebBrowser.CookieContainer.Add(new Cookie("steamLogin", steamLogin, "/", "." + SteamCommunity));
|
WebBrowser.CookieContainer.Add(new Cookie("steamLogin", steamLogin, "/", "." + SteamCommunityHost));
|
||||||
|
|
||||||
string steamLoginSecure = authResult["tokensecure"].Value;
|
string steamLoginSecure = authResult["tokensecure"].Value;
|
||||||
WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunity));
|
WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunityHost));
|
||||||
|
|
||||||
if (!UnlockParentalAccount(parentalPin).Result) {
|
if (!UnlockParentalAccount(parentalPin).Result) {
|
||||||
return false;
|
return false;
|
||||||
@@ -132,49 +179,71 @@ namespace ArchiSteamFarm {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool?> IsLoggedIn() {
|
internal async Task<bool> AcceptGift(ulong gid) {
|
||||||
HtmlDocument htmlDocument = null;
|
if (gid == 0) {
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
return false;
|
||||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile").ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htmlDocument == null) {
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
return false;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='account_pulldown']");
|
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||||
return htmlNode != null;
|
if (string.IsNullOrEmpty(sessionID)) {
|
||||||
|
Logging.LogNullError("sessionID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string request = SteamCommunityURL + "/gifts/" + gid + "/acceptunpack";
|
||||||
|
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
||||||
|
{ "sessionid", sessionID }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && !result; i++) {
|
||||||
|
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> RefreshSessionIfNeeded() {
|
internal async Task<bool> JoinGroup(ulong groupID) {
|
||||||
DateTime now = DateTime.Now;
|
if (groupID == 0) {
|
||||||
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||||
|
if (string.IsNullOrEmpty(sessionID)) {
|
||||||
|
Logging.LogNullError("sessionID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string request = SteamCommunityURL + "/gid/" + groupID;
|
||||||
|
Dictionary<string, string> data = new Dictionary<string, string>(2) {
|
||||||
|
{ "sessionID", sessionID },
|
||||||
|
{ "action", "join" }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && !result; i++) {
|
||||||
|
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SessionSemaphore.WaitAsync().ConfigureAwait(false);
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
|
return false;
|
||||||
now = DateTime.Now;
|
|
||||||
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
|
||||||
SessionSemaphore.Release();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result;
|
|
||||||
|
|
||||||
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
|
||||||
if (isLoggedIn.GetValueOrDefault(true)) {
|
|
||||||
result = true;
|
|
||||||
now = DateTime.Now;
|
|
||||||
LastSessionRefreshCheck = now;
|
|
||||||
} else {
|
|
||||||
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
|
|
||||||
result = await Bot.RefreshSession().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionSemaphore.Release();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
|
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
|
||||||
@@ -185,7 +254,7 @@ namespace ArchiSteamFarm {
|
|||||||
string request = SteamCommunityURL + "/my/games/?xml=1";
|
string request = SteamCommunityURL + "/my/games/?xml=1";
|
||||||
|
|
||||||
XmlDocument response = null;
|
XmlDocument response = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||||
response = await WebBrowser.UrlGetToXML(request).ConfigureAwait(false);
|
response = await WebBrowser.UrlGetToXML(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +264,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
XmlNodeList xmlNodeList = response.SelectNodes("gamesList/games/game");
|
XmlNodeList xmlNodeList = response.SelectNodes("gamesList/games/game");
|
||||||
if (xmlNodeList == null || xmlNodeList.Count == 0) {
|
if ((xmlNodeList == null) || (xmlNodeList.Count == 0)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +291,47 @@ namespace ArchiSteamFarm {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal List<Steam.TradeOffer> GetTradeOffers() {
|
internal Dictionary<uint, string> GetOwnedGames(ulong steamID) {
|
||||||
|
if ((steamID == 0) || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyValue response = null;
|
||||||
|
using (dynamic iPlayerService = WebAPI.GetInterface("IPlayerService", Bot.BotConfig.SteamApiKey)) {
|
||||||
|
iPlayerService.Timeout = Timeout;
|
||||||
|
|
||||||
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||||
|
try {
|
||||||
|
response = iPlayerService.GetOwnedGames(
|
||||||
|
steamid: steamID,
|
||||||
|
include_appinfo: 1,
|
||||||
|
secure: !Program.GlobalConfig.ForceHttp
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logging.LogGenericException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response == null) {
|
||||||
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<uint, string> result = new Dictionary<uint, string>(response["games"].Children.Count);
|
||||||
|
foreach (KeyValue game in response["games"].Children) {
|
||||||
|
uint appID = (uint) game["appid"].AsUnsignedLong();
|
||||||
|
if (appID == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[appID] = game["name"].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal HashSet<Steam.TradeOffer> GetTradeOffers() {
|
||||||
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -231,11 +340,12 @@ namespace ArchiSteamFarm {
|
|||||||
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
|
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
|
||||||
iEconService.Timeout = Timeout;
|
iEconService.Timeout = Timeout;
|
||||||
|
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||||
try {
|
try {
|
||||||
response = iEconService.GetTradeOffers(
|
response = iEconService.GetTradeOffers(
|
||||||
get_received_offers: 1,
|
get_received_offers: 1,
|
||||||
active_only: 1,
|
active_only: 1,
|
||||||
|
get_descriptions: 1,
|
||||||
secure: !Program.GlobalConfig.ForceHttp
|
secure: !Program.GlobalConfig.ForceHttp
|
||||||
);
|
);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -249,74 +359,92 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Steam.TradeOffer> result = new List<Steam.TradeOffer>();
|
Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>> descriptions = new Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>>();
|
||||||
|
foreach (KeyValue description in response["descriptions"].Children) {
|
||||||
|
ulong classID = description["classid"].AsUnsignedLong();
|
||||||
|
if (classID == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong instanceID = description["instanceid"].AsUnsignedLong();
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(classID, instanceID);
|
||||||
|
if (descriptions.ContainsKey(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint appID = 0;
|
||||||
|
Steam.Item.EType type = Steam.Item.EType.Unknown;
|
||||||
|
|
||||||
|
string hashName = description["market_hash_name"].Value;
|
||||||
|
if (!string.IsNullOrEmpty(hashName)) {
|
||||||
|
appID = GetAppIDFromMarketHashName(hashName);
|
||||||
|
}
|
||||||
|
|
||||||
|
string descriptionType = description["type"].Value;
|
||||||
|
if (!string.IsNullOrEmpty(descriptionType)) {
|
||||||
|
type = GetItemType(descriptionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptions[key] = new Tuple<uint, Steam.Item.EType>(appID, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<Steam.TradeOffer> result = new HashSet<Steam.TradeOffer>();
|
||||||
foreach (KeyValue trade in response["trade_offers_received"].Children) {
|
foreach (KeyValue trade in response["trade_offers_received"].Children) {
|
||||||
Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
|
Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
|
||||||
tradeofferid = trade["tradeofferid"].AsString(),
|
TradeOfferID = trade["tradeofferid"].AsUnsignedLong(),
|
||||||
accountid_other = (uint) trade["accountid_other"].AsUnsignedLong(), // TODO: Correct this when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released
|
OtherSteamID3 = (uint) trade["accountid_other"].AsUnsignedLong(),
|
||||||
trade_offer_state = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
|
State = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (KeyValue item in trade["items_to_give"].Children) {
|
foreach (KeyValue item in trade["items_to_give"].Children) {
|
||||||
tradeOffer.items_to_give.Add(new Steam.Item {
|
Steam.Item steamItem = new Steam.Item {
|
||||||
appid = item["appid"].AsString(),
|
AppID = (uint) item["appid"].AsUnsignedLong(),
|
||||||
contextid = item["contextid"].AsString(),
|
ContextID = item["contextid"].AsUnsignedLong(),
|
||||||
assetid = item["assetid"].AsString(),
|
AssetID = item["assetid"].AsUnsignedLong(),
|
||||||
classid = item["classid"].AsString(),
|
ClassID = item["classid"].AsUnsignedLong(),
|
||||||
instanceid = item["instanceid"].AsString(),
|
InstanceID = item["instanceid"].AsUnsignedLong(),
|
||||||
amount = item["amount"].AsString(),
|
Amount = (uint) item["amount"].AsUnsignedLong()
|
||||||
});
|
};
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
||||||
|
|
||||||
|
Tuple<uint, Steam.Item.EType> description;
|
||||||
|
if (descriptions.TryGetValue(key, out description)) {
|
||||||
|
steamItem.RealAppID = description.Item1;
|
||||||
|
steamItem.Type = description.Item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
tradeOffer.ItemsToGive.Add(steamItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (KeyValue item in trade["items_to_receive"].Children) {
|
foreach (KeyValue item in trade["items_to_receive"].Children) {
|
||||||
tradeOffer.items_to_receive.Add(new Steam.Item {
|
Steam.Item steamItem = new Steam.Item {
|
||||||
appid = item["appid"].AsString(),
|
AppID = (uint) item["appid"].AsUnsignedLong(),
|
||||||
contextid = item["contextid"].AsString(),
|
ContextID = item["contextid"].AsUnsignedLong(),
|
||||||
assetid = item["assetid"].AsString(),
|
AssetID = item["assetid"].AsUnsignedLong(),
|
||||||
classid = item["classid"].AsString(),
|
ClassID = item["classid"].AsUnsignedLong(),
|
||||||
instanceid = item["instanceid"].AsString(),
|
InstanceID = item["instanceid"].AsUnsignedLong(),
|
||||||
amount = item["amount"].AsString(),
|
Amount = (uint) item["amount"].AsUnsignedLong()
|
||||||
});
|
};
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
||||||
|
|
||||||
|
Tuple<uint, Steam.Item.EType> description;
|
||||||
|
if (descriptions.TryGetValue(key, out description)) {
|
||||||
|
steamItem.RealAppID = description.Item1;
|
||||||
|
steamItem.Type = description.Item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
tradeOffer.ItemsToReceive.Add(steamItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Add(tradeOffer);
|
result.Add(tradeOffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> JoinClan(ulong clanID) {
|
|
||||||
if (clanID == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
|
||||||
if (string.IsNullOrEmpty(sessionID)) {
|
|
||||||
Logging.LogNullError("sessionID");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string request = SteamCommunityURL + "/gid/" + clanID;
|
|
||||||
|
|
||||||
Dictionary<string, string> data = new Dictionary<string, string>(2) {
|
|
||||||
{"sessionID", sessionID},
|
|
||||||
{"action", "join"}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool result = false;
|
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
|
||||||
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
|
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
|
||||||
if (tradeID == 0) {
|
if (tradeID == 0) {
|
||||||
return false;
|
return false;
|
||||||
@@ -334,61 +462,140 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
string referer = SteamCommunityURL + "/tradeoffer/" + tradeID;
|
string referer = SteamCommunityURL + "/tradeoffer/" + tradeID;
|
||||||
string request = referer + "/accept";
|
string request = referer + "/accept";
|
||||||
|
|
||||||
Dictionary<string, string> data = new Dictionary<string, string>(3) {
|
Dictionary<string, string> data = new Dictionary<string, string>(3) {
|
||||||
{"sessionid", sessionID},
|
{ "sessionid", sessionID },
|
||||||
{"serverid", "1"},
|
{ "serverid", "1" },
|
||||||
{"tradeofferid", tradeID.ToString()}
|
{ "tradeofferid", tradeID.ToString() }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && !result; i++) {
|
||||||
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result) {
|
if (result) {
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<List<Steam.Item>> GetMyTradableInventory() {
|
internal async Task<HashSet<Steam.Item>> GetMyTradableInventory() {
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
JObject jObject = null;
|
HashSet<Steam.Item> result = new HashSet<Steam.Item>();
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
|
|
||||||
jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/753/6?trading=1").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jObject == null) {
|
ushort nextPage = 0;
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
while (true) {
|
||||||
return null;
|
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1&start=" + nextPage;
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerable<JToken> jTokens = jObject.SelectTokens("$.rgInventory.*");
|
JObject jObject = null;
|
||||||
if (jTokens == null) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (jObject == null); i++) {
|
||||||
Logging.LogNullError("jTokens", Bot.BotName);
|
jObject = await WebBrowser.UrlGetToJObject(request).ConfigureAwait(false);
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
List<Steam.Item> result = new List<Steam.Item>();
|
if (jObject == null) {
|
||||||
foreach (JToken jToken in jTokens) {
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
try {
|
return null;
|
||||||
result.Add(JsonConvert.DeserializeObject<Steam.Item>(jToken.ToString()));
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
Logging.LogGenericException(e, Bot.BotName);
|
IEnumerable<JToken> descriptions = jObject.SelectTokens("$.rgDescriptions.*");
|
||||||
|
if (descriptions == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>> descriptionMap = new Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>>();
|
||||||
|
foreach (JToken description in descriptions) {
|
||||||
|
string classIDString = description["classid"].ToString();
|
||||||
|
if (string.IsNullOrEmpty(classIDString)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong classID;
|
||||||
|
if (!ulong.TryParse(classIDString, out classID) || (classID == 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string instanceIDString = description["instanceid"].ToString();
|
||||||
|
if (string.IsNullOrEmpty(instanceIDString)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong instanceID;
|
||||||
|
if (!ulong.TryParse(instanceIDString, out instanceID)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(classID, instanceID);
|
||||||
|
if (descriptionMap.ContainsKey(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint appID = 0;
|
||||||
|
Steam.Item.EType type = Steam.Item.EType.Unknown;
|
||||||
|
|
||||||
|
string hashName = description["market_hash_name"].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(hashName)) {
|
||||||
|
appID = GetAppIDFromMarketHashName(hashName);
|
||||||
|
}
|
||||||
|
|
||||||
|
string descriptionType = description["type"].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(descriptionType)) {
|
||||||
|
type = GetItemType(descriptionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionMap[key] = new Tuple<uint, Steam.Item.EType>(appID, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<JToken> items = jObject.SelectTokens("$.rgInventory.*");
|
||||||
|
if (items == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (JToken item in items) {
|
||||||
|
|
||||||
|
Steam.Item steamItem;
|
||||||
|
|
||||||
|
try {
|
||||||
|
steamItem = JsonConvert.DeserializeObject<Steam.Item>(item.ToString());
|
||||||
|
} catch (JsonException e) {
|
||||||
|
Logging.LogGenericException(e, Bot.BotName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (steamItem == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
||||||
|
|
||||||
|
Tuple<uint, Steam.Item.EType> description;
|
||||||
|
if (descriptionMap.TryGetValue(key, out description)) {
|
||||||
|
steamItem.RealAppID = description.Item1;
|
||||||
|
steamItem.Type = description.Item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(steamItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool more;
|
||||||
|
if (!bool.TryParse(jObject["more"].ToString(), out more) || !more) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ushort.TryParse(jObject["more_start"].ToString(), out nextPage)) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> SendTradeOffer(List<Steam.Item> inventory, ulong partnerID, string token = null) {
|
internal async Task<bool> SendTradeOffer(HashSet<Steam.Item> inventory, ulong partnerID, string token = null) {
|
||||||
if (inventory == null || inventory.Count == 0 || partnerID == 0) {
|
if ((inventory == null) || (inventory.Count == 0) || (partnerID == 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,50 +609,54 @@ namespace ArchiSteamFarm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Steam.TradeOfferRequest> trades = new List<Steam.TradeOfferRequest>(1 + inventory.Count / Trading.MaxItemsPerTrade);
|
Steam.TradeOfferRequest singleTrade = new Steam.TradeOfferRequest();
|
||||||
|
HashSet<Steam.TradeOfferRequest> trades = new HashSet<Steam.TradeOfferRequest> { singleTrade };
|
||||||
|
|
||||||
Steam.TradeOfferRequest singleTrade = null;
|
byte itemID = 0;
|
||||||
for (ushort i = 0; i < inventory.Count; i++) {
|
foreach (Steam.Item item in inventory) {
|
||||||
if (i % Trading.MaxItemsPerTrade == 0) {
|
if (itemID >= Trading.MaxItemsPerTrade) {
|
||||||
if (trades.Count >= Trading.MaxTradesPerAccount) {
|
if (trades.Count >= Trading.MaxTradesPerAccount) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
singleTrade = new Steam.TradeOfferRequest();
|
singleTrade = new Steam.TradeOfferRequest();
|
||||||
trades.Add(singleTrade);
|
trades.Add(singleTrade);
|
||||||
|
itemID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Steam.Item item = inventory[i];
|
singleTrade.ItemsToGive.Assets.Add(new Steam.Item {
|
||||||
singleTrade.me.assets.Add(new Steam.Item() {
|
AppID = Steam.Item.SteamAppID,
|
||||||
appid = "753",
|
ContextID = Steam.Item.SteamContextID,
|
||||||
contextid = "6",
|
Amount = item.Amount,
|
||||||
amount = item.amount,
|
AssetID = item.AssetID
|
||||||
assetid = item.id
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itemID++;
|
||||||
}
|
}
|
||||||
|
|
||||||
string referer = SteamCommunityURL + "/tradeoffer/new";
|
string referer = SteamCommunityURL + "/tradeoffer/new";
|
||||||
string request = referer + "/send";
|
string request = referer + "/send";
|
||||||
|
|
||||||
foreach (Steam.TradeOfferRequest trade in trades) {
|
foreach (Steam.TradeOfferRequest trade in trades) {
|
||||||
Dictionary<string, string> data = new Dictionary<string, string>(6) {
|
Dictionary<string, string> data = new Dictionary<string, string>(6) {
|
||||||
{"sessionid", sessionID},
|
{ "sessionid", sessionID },
|
||||||
{"serverid", "1"},
|
{ "serverid", "1" },
|
||||||
{"partner", partnerID.ToString()},
|
{ "partner", partnerID.ToString() },
|
||||||
{"tradeoffermessage", "Sent by ASF"},
|
{ "tradeoffermessage", "Sent by ASF" },
|
||||||
{"json_tradeoffer", JsonConvert.SerializeObject(trade)},
|
{ "json_tradeoffer", JsonConvert.SerializeObject(trade) },
|
||||||
{"trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : $"{{\"trade_offer_access_token\":\"{token}\"}}"}
|
{ "trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : $"{{\"trade_offer_access_token\":\"{token}\"}}" }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && !result; i++) {
|
||||||
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result) {
|
if (result) {
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
continue;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -460,17 +671,19 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string request = SteamCommunityURL + "/my/badges?p=" + page;
|
||||||
|
|
||||||
HtmlDocument htmlDocument = null;
|
HtmlDocument htmlDocument = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (htmlDocument == null); i++) {
|
||||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/badges?p=" + page).ConfigureAwait(false);
|
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htmlDocument == null) {
|
if (htmlDocument != null) {
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
return htmlDocument;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return htmlDocument;
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
|
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
|
||||||
@@ -482,17 +695,19 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string request = SteamCommunityURL + "/my/gamecards/" + appID;
|
||||||
|
|
||||||
HtmlDocument htmlDocument = null;
|
HtmlDocument htmlDocument = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (htmlDocument == null); i++) {
|
||||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/gamecards/" + appID).ConfigureAwait(false);
|
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htmlDocument == null) {
|
if (htmlDocument != null) {
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
return htmlDocument;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return htmlDocument;
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> MarkInventory() {
|
internal async Task<bool> MarkInventory() {
|
||||||
@@ -500,50 +715,62 @@ namespace ArchiSteamFarm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string request = SteamCommunityURL + "/my/inventory";
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && !result; i++) {
|
||||||
result = await WebBrowser.UrlGet(SteamCommunityURL + "/my/inventory").ConfigureAwait(false);
|
result = await WebBrowser.UrlHead(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result) {
|
if (result) {
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> AcceptGift(ulong gid) {
|
private async Task<bool?> IsLoggedIn() {
|
||||||
if (gid == 0) {
|
string request = SteamCommunityURL + "/my/profile";
|
||||||
return false;
|
|
||||||
|
Uri uri = null;
|
||||||
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (uri == null); i++) {
|
||||||
|
uri = await WebBrowser.UrlHeadToUri(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (uri != null) {
|
||||||
return false;
|
return !uri.AbsolutePath.StartsWith("/login", StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
if (string.IsNullOrEmpty(sessionID)) {
|
return null;
|
||||||
Logging.LogNullError("sessionID");
|
}
|
||||||
return false;
|
|
||||||
|
private async Task<bool> RefreshSessionIfNeeded() {
|
||||||
|
if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
string request = SteamCommunityURL + "/gifts/" + gid + "/acceptunpack";
|
await SessionSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
|
||||||
{ "sessionid", sessionID }
|
|
||||||
};
|
|
||||||
|
|
||||||
bool result = false;
|
if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
SessionSemaphore.Release();
|
||||||
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result) {
|
bool result;
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
||||||
return false;
|
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
||||||
|
if (isLoggedIn.GetValueOrDefault(true)) {
|
||||||
|
result = true;
|
||||||
|
LastSessionRefreshCheck = DateTime.Now;
|
||||||
|
} else {
|
||||||
|
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
|
||||||
|
result = await Bot.RefreshSession().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
SessionSemaphore.Release();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UnlockParentalAccount(string parentalPin) {
|
private async Task<bool> UnlockParentalAccount(string parentalPin) {
|
||||||
@@ -552,16 +779,15 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
|
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
|
||||||
|
|
||||||
|
string request = SteamCommunityURL + "/parental/ajaxunlock";
|
||||||
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
||||||
{ "pin", parentalPin }
|
{ "pin", parentalPin }
|
||||||
};
|
};
|
||||||
|
|
||||||
string referer = SteamCommunityURL;
|
|
||||||
string request = referer + "/parental/ajaxunlock";
|
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && !result; i++) {
|
||||||
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
result = await WebBrowser.UrlPost(request, data, SteamCommunityURL).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,8 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
|
// ReSharper disable once ClassCannotBeInstantiated
|
||||||
|
// ReSharper disable once ClassNeverInstantiated.Global
|
||||||
internal sealed class BotConfig {
|
internal sealed class BotConfig {
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal bool Enabled { get; private set; } = false;
|
internal bool Enabled { get; private set; } = false;
|
||||||
@@ -36,10 +38,10 @@ namespace ArchiSteamFarm {
|
|||||||
internal bool StartOnLaunch { get; private set; } = true;
|
internal bool StartOnLaunch { get; private set; } = true;
|
||||||
|
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
internal string SteamLogin { get; set; } = null;
|
internal string SteamLogin { get; set; }
|
||||||
|
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
internal string SteamPassword { get; set; } = null;
|
internal string SteamPassword { get; set; }
|
||||||
|
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
internal string SteamParentalPIN { get; set; } = "0";
|
internal string SteamParentalPIN { get; set; } = "0";
|
||||||
@@ -68,6 +70,9 @@ namespace ArchiSteamFarm {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal bool AcceptGifts { get; private set; } = false;
|
internal bool AcceptGifts { get; private set; } = false;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
internal bool SteamTradeMatcher { get; private set; } = false;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal bool ForwardKeysToOtherBots { get; private set; } = false;
|
internal bool ForwardKeysToOtherBots { get; private set; } = false;
|
||||||
|
|
||||||
@@ -96,7 +101,7 @@ namespace ArchiSteamFarm {
|
|||||||
internal string CustomGamePlayedWhileIdle { get; private set; } = null;
|
internal string CustomGamePlayedWhileIdle { get; private set; } = null;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint>() { 0 };
|
internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint> { 0 };
|
||||||
|
|
||||||
|
|
||||||
internal static BotConfig Load(string filePath) {
|
internal static BotConfig Load(string filePath) {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using SteamAuth;
|
using SteamAuth;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
@@ -93,7 +94,7 @@ namespace ArchiSteamFarm {
|
|||||||
// This constructor is used when creating new database
|
// This constructor is used when creating new database
|
||||||
private BotDatabase(string filePath) {
|
private BotDatabase(string filePath) {
|
||||||
if (string.IsNullOrEmpty(filePath)) {
|
if (string.IsNullOrEmpty(filePath)) {
|
||||||
throw new ArgumentNullException("filePath");
|
throw new ArgumentNullException(nameof(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePath = filePath;
|
FilePath = filePath;
|
||||||
@@ -101,6 +102,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This constructor is used only by deserializer
|
// This constructor is used only by deserializer
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
private BotDatabase() { }
|
private BotDatabase() { }
|
||||||
|
|
||||||
internal void Save() {
|
internal void Save() {
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ using System.Threading.Tasks;
|
|||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal sealed class CardsFarmer {
|
internal sealed class CardsFarmer {
|
||||||
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
|
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
|
||||||
internal readonly HashSet<uint> CurrentGamesFarming = new HashSet<uint>();
|
internal readonly ConcurrentHashSet<uint> CurrentGamesFarming = new ConcurrentHashSet<uint>();
|
||||||
|
|
||||||
private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false);
|
private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
|
||||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
private readonly SemaphoreSlim FarmingSemaphore = new SemaphoreSlim(1);
|
||||||
private readonly Bot Bot;
|
private readonly Bot Bot;
|
||||||
private readonly Timer Timer;
|
private readonly Timer Timer;
|
||||||
|
|
||||||
@@ -48,12 +48,12 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
internal CardsFarmer(Bot bot) {
|
internal CardsFarmer(Bot bot) {
|
||||||
if (bot == null) {
|
if (bot == null) {
|
||||||
throw new ArgumentNullException("bot");
|
throw new ArgumentNullException(nameof(bot));
|
||||||
}
|
}
|
||||||
|
|
||||||
Bot = bot;
|
Bot = bot;
|
||||||
|
|
||||||
if (Timer == null && Program.GlobalConfig.IdleFarmingPeriod > 0) {
|
if ((Timer == null) && (Program.GlobalConfig.IdleFarmingPeriod > 0)) {
|
||||||
Timer = new Timer(
|
Timer = new Timer(
|
||||||
async e => await CheckGamesForFarming().ConfigureAwait(false),
|
async e => await CheckGamesForFarming().ConfigureAwait(false),
|
||||||
null,
|
null,
|
||||||
@@ -84,15 +84,15 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
if (NowFarming || ManualMode) {
|
if (NowFarming || ManualMode) {
|
||||||
Semaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
|
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
|
||||||
Semaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
||||||
Logging.LogGenericInfo("We don't have anything to farm on this account!", Bot.BotName);
|
Logging.LogGenericInfo("We don't have anything to farm on this account!", Bot.BotName);
|
||||||
await Bot.OnFarmingFinished(false).ConfigureAwait(false);
|
await Bot.OnFarmingFinished(false).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
@@ -100,9 +100,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
Logging.LogGenericInfo("We have a total of " + GamesToFarm.Count + " games to farm on this account...", Bot.BotName);
|
Logging.LogGenericInfo("We have a total of " + GamesToFarm.Count + " games to farm on this account...", Bot.BotName);
|
||||||
NowFarming = true;
|
NowFarming = true;
|
||||||
Semaphore.Release(); // From this point we allow other calls to shut us down
|
FarmingSemaphore.Release(); // From this point we allow other calls to shut us down
|
||||||
|
|
||||||
bool farmedSomething = false;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Now the algorithm used for farming depends on whether account is restricted or not
|
// Now the algorithm used for farming depends on whether account is restricted or not
|
||||||
@@ -114,7 +112,6 @@ namespace ArchiSteamFarm {
|
|||||||
while (gamesToFarmSolo.Count > 0) {
|
while (gamesToFarmSolo.Count > 0) {
|
||||||
uint appID = gamesToFarmSolo.First();
|
uint appID = gamesToFarmSolo.First();
|
||||||
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
||||||
farmedSomething = true;
|
|
||||||
gamesToFarmSolo.Remove(appID);
|
gamesToFarmSolo.Remove(appID);
|
||||||
gamesToFarmSolo.TrimExcess();
|
gamesToFarmSolo.TrimExcess();
|
||||||
} else {
|
} else {
|
||||||
@@ -136,21 +133,20 @@ namespace ArchiSteamFarm {
|
|||||||
while (GamesToFarm.Count > 0) {
|
while (GamesToFarm.Count > 0) {
|
||||||
uint appID = GamesToFarm.Keys.FirstOrDefault();
|
uint appID = GamesToFarm.Keys.FirstOrDefault();
|
||||||
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
||||||
farmedSomething = true;
|
continue;
|
||||||
} else {
|
|
||||||
NowFarming = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NowFarming = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (await IsAnythingToFarm().ConfigureAwait(false));
|
} while (await IsAnythingToFarm().ConfigureAwait(false));
|
||||||
|
|
||||||
CurrentGamesFarming.Clear();
|
CurrentGamesFarming.ClearAndTrim();
|
||||||
CurrentGamesFarming.TrimExcess();
|
|
||||||
NowFarming = false;
|
NowFarming = false;
|
||||||
|
|
||||||
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
|
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
|
||||||
await Bot.OnFarmingFinished(farmedSomething).ConfigureAwait(false);
|
await Bot.OnFarmingFinished(true).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task StopFarming() {
|
internal async Task StopFarming() {
|
||||||
@@ -158,10 +154,10 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
if (!NowFarming) {
|
if (!NowFarming) {
|
||||||
Semaphore.Release();
|
FarmingSemaphore.Release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +165,7 @@ namespace ArchiSteamFarm {
|
|||||||
FarmResetEvent.Set();
|
FarmResetEvent.Set();
|
||||||
|
|
||||||
Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName);
|
Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName);
|
||||||
for (byte i = 0; i < Program.GlobalConfig.HttpTimeout && NowFarming; i++) {
|
for (byte i = 0; (i < Program.GlobalConfig.HttpTimeout) && NowFarming; i++) {
|
||||||
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +175,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
FarmResetEvent.Reset();
|
FarmResetEvent.Reset();
|
||||||
Logging.LogGenericInfo("Farming stopped!", Bot.BotName);
|
Logging.LogGenericInfo("Farming stopped!", Bot.BotName);
|
||||||
Semaphore.Release();
|
FarmingSemaphore.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task RestartFarming() {
|
internal async Task RestartFarming() {
|
||||||
@@ -193,10 +189,8 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HashSet<uint> result = new HashSet<uint>();
|
HashSet<uint> result = new HashSet<uint>();
|
||||||
foreach (KeyValuePair<uint, float> keyValue in gamesToFarm) {
|
foreach (KeyValuePair<uint, float> keyValue in gamesToFarm.Where(keyValue => keyValue.Value >= 2)) {
|
||||||
if (keyValue.Value >= 2) {
|
result.Add(keyValue.Key);
|
||||||
result.Add(keyValue.Key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -215,7 +209,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
byte maxPages = 1;
|
byte maxPages = 1;
|
||||||
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
|
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
|
||||||
if (htmlNodeCollection != null && htmlNodeCollection.Count > 0) {
|
if ((htmlNodeCollection != null) && (htmlNodeCollection.Count > 0)) {
|
||||||
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
|
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
|
||||||
string lastPage = htmlNode.InnerText;
|
string lastPage = htmlNode.InnerText;
|
||||||
if (!string.IsNullOrEmpty(lastPage)) {
|
if (!string.IsNullOrEmpty(lastPage)) {
|
||||||
@@ -229,16 +223,19 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
CheckPage(htmlDocument);
|
CheckPage(htmlDocument);
|
||||||
|
|
||||||
if (maxPages > 1) {
|
if (maxPages <= 1) {
|
||||||
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
|
return GamesToFarm.Count > 0;
|
||||||
List<Task> tasks = new List<Task>(maxPages - 1);
|
|
||||||
for (byte page = 2; page <= maxPages; page++) {
|
|
||||||
byte currentPage = page; // We need a copy of variable being passed when in for loops, as loop will proceed before task is launched
|
|
||||||
tasks.Add(CheckPage(currentPage));
|
|
||||||
}
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
|
||||||
|
|
||||||
|
List<Task> tasks = new List<Task>(maxPages - 1);
|
||||||
|
for (byte page = 2; page <= maxPages; page++) {
|
||||||
|
byte currentPage = page; // We need a copy of variable being passed when in for loops, as loop will proceed before task is launched
|
||||||
|
tasks.Add(CheckPage(currentPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
return GamesToFarm.Count > 0;
|
return GamesToFarm.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,21 +366,15 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (maxHour >= 2) {
|
if (maxHour >= 2) {
|
||||||
CurrentGamesFarming.Clear();
|
CurrentGamesFarming.ClearAndTrim();
|
||||||
CurrentGamesFarming.TrimExcess();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.LogGenericInfo("Now farming: " + string.Join(", ", CurrentGamesFarming), Bot.BotName);
|
Logging.LogGenericInfo("Now farming: " + string.Join(", ", CurrentGamesFarming), Bot.BotName);
|
||||||
if (FarmHours(maxHour, CurrentGamesFarming)) {
|
|
||||||
CurrentGamesFarming.Clear();
|
bool result = FarmHours(maxHour, CurrentGamesFarming);
|
||||||
CurrentGamesFarming.TrimExcess();
|
CurrentGamesFarming.ClearAndTrim();
|
||||||
return true;
|
return result;
|
||||||
} else {
|
|
||||||
CurrentGamesFarming.Clear();
|
|
||||||
CurrentGamesFarming.TrimExcess();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> FarmSolo(uint appID) {
|
private async Task<bool> FarmSolo(uint appID) {
|
||||||
@@ -394,20 +385,22 @@ namespace ArchiSteamFarm {
|
|||||||
CurrentGamesFarming.Add(appID);
|
CurrentGamesFarming.Add(appID);
|
||||||
|
|
||||||
Logging.LogGenericInfo("Now farming: " + appID, Bot.BotName);
|
Logging.LogGenericInfo("Now farming: " + appID, Bot.BotName);
|
||||||
if (await Farm(appID).ConfigureAwait(false)) {
|
|
||||||
CurrentGamesFarming.Clear();
|
bool result = await Farm(appID).ConfigureAwait(false);
|
||||||
CurrentGamesFarming.TrimExcess();
|
CurrentGamesFarming.ClearAndTrim();
|
||||||
float hours;
|
|
||||||
if (GamesToFarm.TryRemove(appID, out hours)) {
|
if (!result) {
|
||||||
TimeSpan timeSpan = TimeSpan.FromHours(hours);
|
|
||||||
Logging.LogGenericInfo("Done farming: " + appID + " after " + timeSpan.ToString(@"hh\:mm") + " hours of playtime!", Bot.BotName);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
CurrentGamesFarming.Clear();
|
|
||||||
CurrentGamesFarming.TrimExcess();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float hours;
|
||||||
|
if (!GamesToFarm.TryRemove(appID, out hours)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSpan timeSpan = TimeSpan.FromHours(hours);
|
||||||
|
Logging.LogGenericInfo("Done farming: " + appID + " after " + timeSpan.ToString(@"hh\:mm") + " hours of playtime!", Bot.BotName);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> Farm(uint appID) {
|
private async Task<bool> Farm(uint appID) {
|
||||||
@@ -420,8 +413,8 @@ namespace ArchiSteamFarm {
|
|||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||||
for (ushort farmingTime = 0; farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
|
for (ushort farmingTime = 0; (farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime) && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
|
||||||
if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||||
success = false;
|
success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -439,8 +432,8 @@ namespace ArchiSteamFarm {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool FarmHours(float maxHour, HashSet<uint> appIDs) {
|
private bool FarmHours(float maxHour, ConcurrentHashSet<uint> appIDs) {
|
||||||
if (maxHour < 0 || appIDs == null || appIDs.Count == 0) {
|
if ((maxHour < 0) || (appIDs == null) || (appIDs.Count == 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +445,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
while (maxHour < 2) {
|
while (maxHour < 2) {
|
||||||
if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||||
success = false;
|
success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,45 +22,37 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using SteamKit2;
|
|
||||||
using SteamKit2.Internal;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal sealed class CMsgClientClanInviteAction : ISteamSerializableMessage {
|
internal sealed class ConcurrentEnumerator<T> : IEnumerator<T> {
|
||||||
internal ulong GroupID { get; set; } = 0;
|
public T Current => Enumerator.Current;
|
||||||
internal bool AcceptInvite { get; set; } = true;
|
|
||||||
|
|
||||||
EMsg ISteamSerializableMessage.GetEMsg() {
|
object IEnumerator.Current => Current;
|
||||||
return EMsg.ClientAcknowledgeClanInvite;
|
|
||||||
|
private readonly IEnumerator<T> Enumerator;
|
||||||
|
private readonly ReaderWriterLockSlim Lock;
|
||||||
|
|
||||||
|
internal ConcurrentEnumerator(ICollection<T> collection, ReaderWriterLockSlim @lock) {
|
||||||
|
if ((collection == null) || (@lock == null)) {
|
||||||
|
throw new ArgumentNullException(nameof(collection) + " || " + nameof(@lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
@lock.EnterReadLock();
|
||||||
|
|
||||||
|
Lock = @lock;
|
||||||
|
Enumerator = collection.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ISteamSerializable.Serialize(Stream stream) {
|
public bool MoveNext() => Enumerator.MoveNext();
|
||||||
if (stream == null) {
|
public void Reset() => Enumerator.Reset();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
public void Dispose() {
|
||||||
BinaryWriter binaryWriter = new BinaryWriter(stream);
|
if (Lock != null) {
|
||||||
binaryWriter.Write(GroupID);
|
Lock.ExitReadLock();
|
||||||
binaryWriter.Write(AcceptInvite);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Logging.LogGenericException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISteamSerializable.Deserialize(Stream stream) {
|
|
||||||
if (stream == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
BinaryReader binaryReader = new BinaryReader(stream);
|
|
||||||
GroupID = binaryReader.ReadUInt64();
|
|
||||||
AcceptInvite = binaryReader.ReadBoolean();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Logging.LogGenericException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
123
ArchiSteamFarm/ConcurrentHashSet.cs
Normal file
123
ArchiSteamFarm/ConcurrentHashSet.cs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
_ _ _ ____ _ _____
|
||||||
|
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||||
|
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||||
|
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||||
|
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||||
|
|
||||||
|
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;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace ArchiSteamFarm {
|
||||||
|
internal sealed class ConcurrentHashSet<T> : ICollection<T>, IDisposable {
|
||||||
|
private readonly HashSet<T> HashSet = new HashSet<T>();
|
||||||
|
private readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||||
|
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
public IEnumerator<T> GetEnumerator() => new ConcurrentEnumerator<T>(HashSet, Lock);
|
||||||
|
|
||||||
|
public int Count {
|
||||||
|
get {
|
||||||
|
Lock.EnterReadLock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return HashSet.Count;
|
||||||
|
} finally {
|
||||||
|
Lock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
|
||||||
|
public bool Add(T item) {
|
||||||
|
Lock.EnterWriteLock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return HashSet.Add(item);
|
||||||
|
} finally {
|
||||||
|
Lock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() {
|
||||||
|
Lock.EnterWriteLock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
HashSet.Clear();
|
||||||
|
} finally {
|
||||||
|
Lock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearAndTrim() {
|
||||||
|
Lock.EnterWriteLock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
HashSet.Clear();
|
||||||
|
HashSet.TrimExcess();
|
||||||
|
} finally {
|
||||||
|
Lock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(T item) {
|
||||||
|
Lock.EnterReadLock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return HashSet.Contains(item);
|
||||||
|
} finally {
|
||||||
|
Lock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(T item) {
|
||||||
|
Lock.EnterWriteLock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return HashSet.Remove(item);
|
||||||
|
} finally {
|
||||||
|
Lock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
if (Lock != null) {
|
||||||
|
Lock.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(T[] array, int arrayIndex) {
|
||||||
|
Lock.EnterReadLock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
HashSet.CopyTo(array, arrayIndex);
|
||||||
|
} finally {
|
||||||
|
Lock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<T>.Add(T item) => Add(item);
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,19 +24,20 @@
|
|||||||
|
|
||||||
using SteamKit2;
|
using SteamKit2;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal static class Debugging {
|
internal static class Debugging {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
|
||||||
internal static readonly bool IsDebugBuild = true;
|
internal static readonly bool IsDebugBuild = true;
|
||||||
#else
|
#else
|
||||||
|
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
|
||||||
internal static readonly bool IsDebugBuild = false;
|
internal static readonly bool IsDebugBuild = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
internal static bool IsReleaseBuild => !IsDebugBuild;
|
internal static bool NetHookAlreadyInitialized { get; set; }
|
||||||
|
|
||||||
internal static bool NetHookAlreadyInitialized { get; set; } = false;
|
|
||||||
|
|
||||||
internal sealed class DebugListener : IDebugListener {
|
internal sealed class DebugListener : IDebugListener {
|
||||||
private readonly string FilePath;
|
private readonly string FilePath;
|
||||||
|
|||||||
@@ -25,11 +25,14 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
|
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated"), SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||||
internal sealed class GlobalConfig {
|
internal sealed class GlobalConfig {
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
internal enum EUpdateChannel : byte {
|
internal enum EUpdateChannel : byte {
|
||||||
Unknown,
|
Unknown,
|
||||||
Stable,
|
Stable,
|
||||||
@@ -55,6 +58,9 @@ namespace ArchiSteamFarm {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal bool AutoUpdates { get; private set; } = true;
|
internal bool AutoUpdates { get; private set; } = true;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
internal bool AutoRestart { get; private set; } = true;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable;
|
internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable;
|
||||||
|
|
||||||
@@ -157,11 +163,13 @@ namespace ArchiSteamFarm {
|
|||||||
globalConfig.HttpTimeout = DefaultHttpTimeout;
|
globalConfig.HttpTimeout = DefaultHttpTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalConfig.WCFPort == 0) {
|
if (globalConfig.WCFPort != 0) {
|
||||||
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
|
return globalConfig;
|
||||||
globalConfig.WCFPort = DefaultWCFPort;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
|
||||||
|
globalConfig.WCFPort = DefaultWCFPort;
|
||||||
|
|
||||||
return globalConfig;
|
return globalConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
@@ -75,7 +76,7 @@ namespace ArchiSteamFarm {
|
|||||||
// This constructor is used when creating new database
|
// This constructor is used when creating new database
|
||||||
private GlobalDatabase(string filePath) {
|
private GlobalDatabase(string filePath) {
|
||||||
if (string.IsNullOrEmpty(filePath)) {
|
if (string.IsNullOrEmpty(filePath)) {
|
||||||
throw new ArgumentNullException("filePath");
|
throw new ArgumentNullException(nameof(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePath = filePath;
|
FilePath = filePath;
|
||||||
@@ -83,6 +84,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This constructor is used only by deserializer
|
// This constructor is used only by deserializer
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
private GlobalDatabase() { }
|
private GlobalDatabase() { }
|
||||||
|
|
||||||
private void Save() {
|
private void Save() {
|
||||||
|
|||||||
@@ -22,20 +22,22 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm.JSON {
|
||||||
internal static class GitHub {
|
internal static class GitHub {
|
||||||
internal sealed class Asset {
|
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global"), SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
|
||||||
[JsonProperty(PropertyName = "name", Required = Required.Always)]
|
|
||||||
internal string Name { get; private set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "browser_download_url", Required = Required.Always)]
|
|
||||||
internal string DownloadURL { get; private set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class ReleaseResponse {
|
internal sealed class ReleaseResponse {
|
||||||
|
internal sealed class Asset {
|
||||||
|
[JsonProperty(PropertyName = "name", Required = Required.Always)]
|
||||||
|
internal string Name { get; private set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "browser_download_url", Required = Required.Always)]
|
||||||
|
internal string DownloadURL { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "tag_name", Required = Required.Always)]
|
[JsonProperty(PropertyName = "tag_name", Required = Required.Always)]
|
||||||
internal string Tag { get; private set; }
|
internal string Tag { get; private set; }
|
||||||
|
|
||||||
|
|||||||
@@ -22,46 +22,176 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using SteamKit2;
|
using SteamKit2;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm.JSON {
|
||||||
internal static class Steam {
|
internal static class Steam {
|
||||||
internal sealed class Item {
|
internal sealed class Item { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
|
||||||
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
|
internal const ushort SteamAppID = 753;
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
internal const byte SteamContextID = 6;
|
||||||
internal string appid { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
internal enum EType : byte {
|
||||||
internal string contextid { get; set; }
|
Unknown,
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
BoosterPack,
|
||||||
internal string assetid { get; set; }
|
Coupon,
|
||||||
|
Gift,
|
||||||
|
SteamGems,
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
Emoticon,
|
||||||
internal string id {
|
FoilTradingCard,
|
||||||
get { return assetid; }
|
ProfileBackground,
|
||||||
set { assetid = value; }
|
TradingCard
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.AllowNull)]
|
internal uint AppID { get; set; }
|
||||||
internal string classid { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(Required = Required.AllowNull)]
|
[JsonProperty(PropertyName = "appid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
internal string instanceid { get; set; }
|
private string AppIDString {
|
||||||
|
get {
|
||||||
|
return AppID.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
set {
|
||||||
internal string amount { get; set; }
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint result;
|
||||||
|
if (!uint.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppID = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong ContextID { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "contextid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
|
private string ContextIDString {
|
||||||
|
get {
|
||||||
|
return ContextID.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong result;
|
||||||
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextID = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong AssetID { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "assetid", Required = Required.DisallowNull)]
|
||||||
|
private string AssetIDString {
|
||||||
|
get {
|
||||||
|
return AssetID.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong result;
|
||||||
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetID = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "id", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
|
private string ID {
|
||||||
|
get { return AssetIDString; }
|
||||||
|
set { AssetIDString = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong ClassID { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "classid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
|
private string ClassIDString {
|
||||||
|
get {
|
||||||
|
return ClassID.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong result;
|
||||||
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassID = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong InstanceID { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
|
private string InstanceIDString {
|
||||||
|
get {
|
||||||
|
return InstanceID.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong result;
|
||||||
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceID = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal uint Amount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "amount", Required = Required.Always), SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
|
private string AmountString {
|
||||||
|
get {
|
||||||
|
return Amount.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint result;
|
||||||
|
if (!uint.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Amount = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal uint RealAppID { get; set; }
|
||||||
|
internal EType Type { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class ItemList {
|
internal sealed class TradeOffer { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
|
||||||
[JsonProperty(Required = Required.Always)]
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
internal List<Steam.Item> assets { get; } = new List<Steam.Item>();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class TradeOffer {
|
|
||||||
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
|
|
||||||
internal enum ETradeOfferState : byte {
|
internal enum ETradeOfferState : byte {
|
||||||
Unknown,
|
Unknown,
|
||||||
Invalid,
|
Invalid,
|
||||||
@@ -77,46 +207,119 @@ namespace ArchiSteamFarm {
|
|||||||
OnHold
|
OnHold
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
internal ulong TradeOfferID { get; set; }
|
||||||
internal string tradeofferid { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(PropertyName = "tradeofferid", Required = Required.Always), SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
internal uint accountid_other { get; set; }
|
private string TradeOfferIDString {
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
|
||||||
internal ETradeOfferState trade_offer_state { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
|
||||||
internal List<Steam.Item> items_to_give { get; } = new List<Steam.Item>();
|
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
|
||||||
internal List<Steam.Item> items_to_receive { get; } = new List<Steam.Item>();
|
|
||||||
|
|
||||||
// Extra
|
|
||||||
private ulong _OtherSteamID64 = 0;
|
|
||||||
internal ulong OtherSteamID64 {
|
|
||||||
get {
|
get {
|
||||||
if (_OtherSteamID64 == 0 && accountid_other != 0) {
|
return TradeOfferID.ToString();
|
||||||
_OtherSteamID64 = new SteamID(accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64();
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _OtherSteamID64;
|
ulong result;
|
||||||
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TradeOfferID = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "accountid_other", Required = Required.Always)]
|
||||||
|
internal uint OtherSteamID3 { private get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "trade_offer_state", Required = Required.Always)]
|
||||||
|
internal ETradeOfferState State { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "items_to_give", Required = Required.Always)]
|
||||||
|
internal HashSet<Item> ItemsToGive { get; } = new HashSet<Item>();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "items_to_receive", Required = Required.Always)]
|
||||||
|
internal HashSet<Item> ItemsToReceive { get; } = new HashSet<Item>();
|
||||||
|
|
||||||
|
// Extra
|
||||||
|
internal ulong OtherSteamID64 => OtherSteamID3 == 0 ? 0 : new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
|
||||||
|
|
||||||
|
internal bool IsSteamCardsOnlyTradeForUs() => ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamContextID) && ((item.Type == Item.EType.FoilTradingCard) || (item.Type == Item.EType.TradingCard)));
|
||||||
|
|
||||||
|
internal bool IsPotentiallyDupesTradeForUs() {
|
||||||
|
Dictionary<uint, Dictionary<Item.EType, uint>> itemsToGivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
|
||||||
|
foreach (Item item in ItemsToGive) {
|
||||||
|
Dictionary<Item.EType, uint> itemsPerType;
|
||||||
|
if (!itemsToGivePerGame.TryGetValue(item.RealAppID, out itemsPerType)) {
|
||||||
|
itemsPerType = new Dictionary<Item.EType, uint> { [item.Type] = item.Amount };
|
||||||
|
itemsToGivePerGame[item.RealAppID] = itemsPerType;
|
||||||
|
} else {
|
||||||
|
uint amount;
|
||||||
|
if (itemsPerType.TryGetValue(item.Type, out amount)) {
|
||||||
|
itemsPerType[item.Type] = amount + item.Amount;
|
||||||
|
} else {
|
||||||
|
itemsPerType[item.Type] = item.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<uint, Dictionary<Item.EType, uint>> itemsToReceivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
|
||||||
|
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 };
|
||||||
|
itemsToReceivePerGame[item.RealAppID] = itemsPerType;
|
||||||
|
} else {
|
||||||
|
uint amount;
|
||||||
|
if (itemsPerType.TryGetValue(item.Type, out amount)) {
|
||||||
|
itemsPerType[item.Type] = amount + item.Amount;
|
||||||
|
} else {
|
||||||
|
itemsPerType[item.Type] = item.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that amount of items to give is at least amount of items to receive (per game and per type)
|
||||||
|
foreach (KeyValuePair<uint, Dictionary<Item.EType, uint>> itemsPerGame in itemsToGivePerGame) {
|
||||||
|
Dictionary<Item.EType, uint> otherItemsPerType;
|
||||||
|
if (!itemsToReceivePerGame.TryGetValue(itemsPerGame.Key, out otherItemsPerType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<Item.EType, uint> itemsPerType in itemsPerGame.Value) {
|
||||||
|
uint otherAmount;
|
||||||
|
if (!otherItemsPerType.TryGetValue(itemsPerType.Key, out otherAmount)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemsPerType.Value > otherAmount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
internal sealed class TradeOfferRequest {
|
internal sealed class TradeOfferRequest {
|
||||||
[JsonProperty(Required = Required.Always)]
|
internal sealed class ItemList {
|
||||||
internal bool newversion { get; } = true;
|
[JsonProperty(PropertyName = "assets", Required = Required.Always)]
|
||||||
|
internal HashSet<Item> Assets { get; } = new HashSet<Item>();
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(PropertyName = "newversion", Required = Required.Always)]
|
||||||
internal int version { get; } = 2;
|
internal bool NewVersion { get; } = true;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(PropertyName = "version", Required = Required.Always)]
|
||||||
internal Steam.ItemList me { get; } = new Steam.ItemList();
|
internal byte Version { get; } = 2;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(PropertyName = "me", Required = Required.Always)]
|
||||||
internal Steam.ItemList them { get; } = new Steam.ItemList();
|
internal ItemList ItemsToGive { get; } = new ItemList();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "them", Required = Required.Always)]
|
||||||
|
internal ItemList ItemsToReceive { get; } = new ItemList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -36,18 +37,25 @@ namespace ArchiSteamFarm {
|
|||||||
internal static void Init() {
|
internal static void Init() {
|
||||||
LogToFile = Program.GlobalConfig.LogToFile;
|
LogToFile = Program.GlobalConfig.LogToFile;
|
||||||
|
|
||||||
if (LogToFile) {
|
if (!LogToFile) {
|
||||||
lock (FileLock) {
|
return;
|
||||||
try {
|
}
|
||||||
File.Delete(Program.LogFile);
|
|
||||||
} catch (Exception e) {
|
lock (FileLock) {
|
||||||
LogGenericException(e);
|
if (!LogToFile) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
File.Delete(Program.LogFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogToFile = false;
|
||||||
|
LogGenericException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LogGenericWTF(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
internal static void LogGenericWTF(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||||
if (string.IsNullOrEmpty(message)) {
|
if (string.IsNullOrEmpty(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -55,7 +63,7 @@ namespace ArchiSteamFarm {
|
|||||||
Log("[!!] WTF: " + previousMethodName + "() <" + botName + "> " + message + ", WTF?");
|
Log("[!!] WTF: " + previousMethodName + "() <" + botName + "> " + message + ", WTF?");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LogGenericError(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
internal static void LogGenericError(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||||
if (string.IsNullOrEmpty(message)) {
|
if (string.IsNullOrEmpty(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -63,20 +71,25 @@ namespace ArchiSteamFarm {
|
|||||||
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
|
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||||
if (exception == null) {
|
while (true) {
|
||||||
return;
|
if (exception == null) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
|
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
|
||||||
Log("[!] StackTrace:" + Environment.NewLine + exception.StackTrace);
|
Log("[!] StackTrace:" + Environment.NewLine + exception.StackTrace);
|
||||||
|
|
||||||
if (exception.InnerException != null) {
|
if (exception.InnerException != null) {
|
||||||
LogGenericException(exception.InnerException, botName, previousMethodName);
|
exception = exception.InnerException;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LogGenericWarning(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
internal static void LogGenericWarning(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||||
if (string.IsNullOrEmpty(message)) {
|
if (string.IsNullOrEmpty(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -84,7 +97,7 @@ namespace ArchiSteamFarm {
|
|||||||
Log("[!] WARNING: " + previousMethodName + "() <" + botName + "> " + message);
|
Log("[!] WARNING: " + previousMethodName + "() <" + botName + "> " + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LogGenericInfo(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
internal static void LogGenericInfo(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||||
if (string.IsNullOrEmpty(message)) {
|
if (string.IsNullOrEmpty(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -92,7 +105,8 @@ namespace ArchiSteamFarm {
|
|||||||
Log("[*] INFO: " + previousMethodName + "() <" + botName + "> " + message);
|
Log("[*] INFO: " + previousMethodName + "() <" + botName + "> " + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LogNullError(string nullObjectName, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
[SuppressMessage("ReSharper", "ExplicitCallerInfoArgument")]
|
||||||
|
internal static void LogNullError(string nullObjectName, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||||
if (string.IsNullOrEmpty(nullObjectName)) {
|
if (string.IsNullOrEmpty(nullObjectName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -100,8 +114,8 @@ namespace ArchiSteamFarm {
|
|||||||
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
|
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Conditional("DEBUG")]
|
[Conditional("DEBUG"), SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
|
||||||
if (string.IsNullOrEmpty(message)) {
|
if (string.IsNullOrEmpty(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -120,17 +134,26 @@ namespace ArchiSteamFarm {
|
|||||||
if (!Program.ConsoleIsBusy) {
|
if (!Program.ConsoleIsBusy) {
|
||||||
try {
|
try {
|
||||||
Console.Write(loggedMessage);
|
Console.Write(loggedMessage);
|
||||||
} catch { }
|
}
|
||||||
|
catch {
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LogToFile) {
|
if (!LogToFile) {
|
||||||
lock (FileLock) {
|
return;
|
||||||
try {
|
}
|
||||||
File.AppendAllText(Program.LogFile, loggedMessage);
|
|
||||||
} catch (Exception e) {
|
lock (FileLock) {
|
||||||
LogToFile = false;
|
if (!LogToFile) {
|
||||||
LogGenericException(e);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
File.AppendAllText(Program.LogFile, loggedMessage);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogToFile = false;
|
||||||
|
LogGenericException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,13 @@ using Newtonsoft.Json;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using ArchiSteamFarm.JSON;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal static class Program {
|
internal static class Program {
|
||||||
@@ -48,37 +50,36 @@ namespace ArchiSteamFarm {
|
|||||||
WCFHostname
|
WCFHostname
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum EMode : byte {
|
private enum EMode : byte {
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
Unknown,
|
Unknown,
|
||||||
Normal, // Standard most common usage
|
Normal, // Standard most common usage
|
||||||
Client, // WCF client only
|
Client, // WCF client only
|
||||||
Server // Normal + WCF server
|
Server // Normal + WCF server
|
||||||
}
|
}
|
||||||
|
|
||||||
internal const string ASF = "ASF";
|
|
||||||
internal const string ConfigDirectory = "config";
|
internal const string ConfigDirectory = "config";
|
||||||
internal const string DebugDirectory = "debug";
|
internal const string DebugDirectory = "debug";
|
||||||
internal const string LogFile = "log.txt";
|
internal const string LogFile = "log.txt";
|
||||||
internal const string GithubRepo = "JustArchi/ArchiSteamFarm";
|
internal const string GithubRepo = "JustArchi/ArchiSteamFarm";
|
||||||
internal const string GlobalConfigFile = ASF + ".json";
|
|
||||||
internal const string GlobalDatabaseFile = ASF + ".db";
|
|
||||||
|
|
||||||
|
private const string ASF = "ASF";
|
||||||
private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only
|
private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only
|
||||||
|
private const string GlobalConfigFile = ASF + ".json";
|
||||||
|
private const string GlobalDatabaseFile = ASF + ".db";
|
||||||
|
|
||||||
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
|
internal static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
|
||||||
internal static readonly Version Version = Assembly.GetName().Version;
|
|
||||||
|
|
||||||
private static readonly object ConsoleLock = new object();
|
private static readonly object ConsoleLock = new object();
|
||||||
private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1);
|
private static readonly ManualResetEventSlim ShutdownResetEvent = new ManualResetEventSlim(false);
|
||||||
private static readonly ManualResetEvent ShutdownResetEvent = new ManualResetEvent(false);
|
private static readonly string ExecutableFile = Assembly.GetEntryAssembly().Location;
|
||||||
private static readonly string ExecutableFile = Assembly.Location;
|
|
||||||
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
|
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
|
||||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
|
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
|
||||||
private static readonly WCF WCF = new WCF();
|
private static readonly WCF WCF = new WCF();
|
||||||
|
|
||||||
internal static GlobalConfig GlobalConfig { get; private set; }
|
internal static GlobalConfig GlobalConfig { get; private set; }
|
||||||
internal static GlobalDatabase GlobalDatabase { get; private set; }
|
internal static GlobalDatabase GlobalDatabase { get; private set; }
|
||||||
internal static bool ConsoleIsBusy { get; private set; } = false;
|
internal static bool ConsoleIsBusy { get; private set; }
|
||||||
|
|
||||||
private static Timer AutoUpdatesTimer;
|
private static Timer AutoUpdatesTimer;
|
||||||
private static EMode Mode = EMode.Normal;
|
private static EMode Mode = EMode.Normal;
|
||||||
@@ -111,7 +112,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
string response = null;
|
string response = null;
|
||||||
Logging.LogGenericInfo("Checking new version...");
|
Logging.LogGenericInfo("Checking new version...");
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && string.IsNullOrEmpty(response); i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && string.IsNullOrEmpty(response); i++) {
|
||||||
response = await WebBrowser.UrlGetToContent(releaseURL).ConfigureAwait(false);
|
response = await WebBrowser.UrlGetToContent(releaseURL).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +138,7 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (releases == null || releases.Count == 0) {
|
if ((releases == null) || (releases.Count == 0)) {
|
||||||
Logging.LogGenericWarning("Could not check latest version!");
|
Logging.LogGenericWarning("Could not check latest version!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -155,15 +156,19 @@ namespace ArchiSteamFarm {
|
|||||||
Logging.LogGenericInfo("Local version: " + Version + " | Remote version: " + newVersion);
|
Logging.LogGenericInfo("Local version: " + Version + " | Remote version: " + newVersion);
|
||||||
|
|
||||||
if (Version.CompareTo(newVersion) >= 0) { // If local version is the same or newer than remote version
|
if (Version.CompareTo(newVersion) >= 0) { // If local version is the same or newer than remote version
|
||||||
if (AutoUpdatesTimer == null && GlobalConfig.AutoUpdates) {
|
if ((AutoUpdatesTimer != null) || !GlobalConfig.AutoUpdates) {
|
||||||
Logging.LogGenericInfo("ASF will automatically check for new versions every 24 hours");
|
return;
|
||||||
AutoUpdatesTimer = new Timer(
|
|
||||||
async e => await CheckForUpdate().ConfigureAwait(false),
|
|
||||||
null,
|
|
||||||
TimeSpan.FromDays(1), // Delay
|
|
||||||
TimeSpan.FromDays(1) // Period
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logging.LogGenericInfo("ASF will automatically check for new versions every 24 hours");
|
||||||
|
|
||||||
|
AutoUpdatesTimer = new Timer(
|
||||||
|
async e => await CheckForUpdate().ConfigureAwait(false),
|
||||||
|
null,
|
||||||
|
TimeSpan.FromDays(1), // Delay
|
||||||
|
TimeSpan.FromDays(1) // Period
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,15 +190,7 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GitHub.Asset binaryAsset = null;
|
GitHub.ReleaseResponse.Asset binaryAsset = releaseResponse.Assets.FirstOrDefault(asset => !string.IsNullOrEmpty(asset.Name) && asset.Name.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase));
|
||||||
foreach (var asset in releaseResponse.Assets) {
|
|
||||||
if (string.IsNullOrEmpty(asset.Name) || !asset.Name.Equals(ExecutableName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
binaryAsset = asset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (binaryAsset == null) {
|
if (binaryAsset == null) {
|
||||||
Logging.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!");
|
Logging.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!");
|
||||||
@@ -206,7 +203,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
byte[] result = null;
|
byte[] result = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && result == null; i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (result == null); i++) {
|
||||||
Logging.LogGenericInfo("Downloading new version...");
|
Logging.LogGenericInfo("Downloading new version...");
|
||||||
result = await WebBrowser.UrlGetToBytes(binaryAsset.DownloadURL).ConfigureAwait(false);
|
result = await WebBrowser.UrlGetToBytes(binaryAsset.DownloadURL).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -234,7 +231,9 @@ namespace ArchiSteamFarm {
|
|||||||
try {
|
try {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
File.Delete(newExeFile);
|
File.Delete(newExeFile);
|
||||||
} catch { }
|
} catch {
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,14 +246,23 @@ namespace ArchiSteamFarm {
|
|||||||
// Cleanup
|
// Cleanup
|
||||||
File.Move(oldExeFile, ExecutableFile);
|
File.Move(oldExeFile, ExecutableFile);
|
||||||
File.Delete(newExeFile);
|
File.Delete(newExeFile);
|
||||||
} catch { }
|
} catch {
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.LogGenericInfo("Update process is finished! ASF will now restart itself...");
|
Logging.LogGenericInfo("Update process finished!");
|
||||||
await Utilities.SleepAsync(5000);
|
|
||||||
|
|
||||||
Restart();
|
if (GlobalConfig.AutoRestart) {
|
||||||
|
Logging.LogGenericInfo("Restarting...");
|
||||||
|
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||||
|
Restart();
|
||||||
|
} else {
|
||||||
|
Logging.LogGenericInfo("Exiting...");
|
||||||
|
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||||
|
Exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Exit(int exitCode = 0) {
|
internal static void Exit(int exitCode = 0) {
|
||||||
@@ -272,14 +280,6 @@ namespace ArchiSteamFarm {
|
|||||||
Exit();
|
Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task LimitSteamRequestsAsync() {
|
|
||||||
await SteamSemaphore.WaitAsync().ConfigureAwait(false);
|
|
||||||
Task.Run(async () => {
|
|
||||||
await Utilities.SleepAsync(GlobalConfig.LoginLimiterDelay * 1000).ConfigureAwait(false);
|
|
||||||
SteamSemaphore.Release();
|
|
||||||
}).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static string GetUserInput(EUserInputType userInputType, string botName = null, string extraInformation = null) {
|
internal static string GetUserInput(EUserInputType userInputType, string botName = null, string extraInformation = null) {
|
||||||
if (userInputType == EUserInputType.Unknown) {
|
if (userInputType == EUserInputType.Unknown) {
|
||||||
return null;
|
return null;
|
||||||
@@ -329,6 +329,7 @@ namespace ArchiSteamFarm {
|
|||||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter not documented yet value of \"" + userInputType + "\": ");
|
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter not documented yet value of \"" + userInputType + "\": ");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = Console.ReadLine();
|
result = Console.ReadLine();
|
||||||
if (!Console.IsOutputRedirected) {
|
if (!Console.IsOutputRedirected) {
|
||||||
Console.Clear(); // For security purposes
|
Console.Clear(); // For security purposes
|
||||||
@@ -340,10 +341,8 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal static void OnBotShutdown() {
|
internal static void OnBotShutdown() {
|
||||||
foreach (Bot bot in Bot.Bots.Values) {
|
if (Bot.Bots.Values.Any(bot => bot.KeepRunning)) {
|
||||||
if (bot.KeepRunning) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WCF.IsServerRunning()) {
|
if (WCF.IsServerRunning()) {
|
||||||
@@ -377,7 +376,11 @@ namespace ArchiSteamFarm {
|
|||||||
WebBrowser = new WebBrowser("Main");
|
WebBrowser = new WebBrowser("Main");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ParseArgs(string[] args) {
|
private static void ParseArgs(IEnumerable<string> args) {
|
||||||
|
if (args == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (string arg in args) {
|
foreach (string arg in args) {
|
||||||
switch (arg) {
|
switch (arg) {
|
||||||
case "--client":
|
case "--client":
|
||||||
@@ -388,7 +391,7 @@ namespace ArchiSteamFarm {
|
|||||||
WCF.StartServer();
|
WCF.StartServer();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (arg.StartsWith("--")) {
|
if (arg.StartsWith("--", StringComparison.Ordinal)) {
|
||||||
Logging.LogGenericWarning("Unrecognized parameter: " + arg);
|
Logging.LogGenericWarning("Unrecognized parameter: " + arg);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -413,7 +416,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
|
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
|
||||||
if (sender == null || args == null) {
|
if ((sender == null) || (args == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,14 +424,14 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
|
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
|
||||||
if (sender == null || args == null) {
|
if ((sender == null) || (args == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.LogGenericException(args.Exception);
|
Logging.LogGenericException(args.Exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Init(string[] args) {
|
private static void Init(IEnumerable<string> args) {
|
||||||
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
|
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
|
||||||
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
|
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
|
||||||
|
|
||||||
@@ -440,7 +443,7 @@ namespace ArchiSteamFarm {
|
|||||||
if (Debugging.IsDebugBuild) {
|
if (Debugging.IsDebugBuild) {
|
||||||
|
|
||||||
// Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up
|
// Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up
|
||||||
for (var i = 0; i < 4; i++) {
|
for (byte i = 0; i < 4; i++) {
|
||||||
Directory.SetCurrentDirectory("..");
|
Directory.SetCurrentDirectory("..");
|
||||||
if (Directory.Exists(ConfigDirectory)) {
|
if (Directory.Exists(ConfigDirectory)) {
|
||||||
break;
|
break;
|
||||||
@@ -461,7 +464,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
Directory.CreateDirectory(DebugDirectory);
|
Directory.CreateDirectory(DebugDirectory);
|
||||||
|
|
||||||
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(Program.DebugDirectory, "debug.txt")));
|
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(DebugDirectory, "debug.txt")));
|
||||||
SteamKit2.DebugLog.Enabled = true;
|
SteamKit2.DebugLog.Enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,8 +492,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
bool isRunning = false;
|
bool isRunning = false;
|
||||||
|
|
||||||
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.json")) {
|
foreach (string botName in Directory.EnumerateFiles(ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension)) {
|
||||||
string botName = Path.GetFileNameWithoutExtension(configFile);
|
|
||||||
switch (botName) {
|
switch (botName) {
|
||||||
case ASF:
|
case ASF:
|
||||||
case "example":
|
case "example":
|
||||||
@@ -499,8 +501,10 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Bot bot = new Bot(botName);
|
Bot bot = new Bot(botName);
|
||||||
if (bot.BotConfig != null && bot.BotConfig.Enabled) {
|
if ((bot.BotConfig != null) && bot.BotConfig.Enabled) {
|
||||||
isRunning = true;
|
if (bot.BotConfig.StartOnLaunch) {
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
|
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
|
||||||
}
|
}
|
||||||
@@ -516,7 +520,7 @@ namespace ArchiSteamFarm {
|
|||||||
Init(args);
|
Init(args);
|
||||||
|
|
||||||
// Wait for signal to shutdown
|
// Wait for signal to shutdown
|
||||||
ShutdownResetEvent.WaitOne();
|
ShutdownResetEvent.Wait();
|
||||||
|
|
||||||
// We got a signal to shutdown
|
// We got a signal to shutdown
|
||||||
Exit();
|
Exit();
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
// General Information about an assembly is controlled through the following
|
||||||
// set of attributes. Change these attribute values to modify the information
|
// set of attributes. Change these attribute values to modify the information
|
||||||
// associated with an assembly.
|
// associated with an assembly.
|
||||||
[assembly: AssemblyTitle("ArchiSteamFarm")]
|
[assembly: AssemblyTitle("ArchiSteamFarm")]
|
||||||
@@ -14,8 +13,8 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
// to COM components. If you need to access a type in this assembly from
|
// to COM components. If you need to access a type in this assembly from
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
@@ -25,12 +24,12 @@ using System.Runtime.InteropServices;
|
|||||||
// Version information for an assembly consists of the following four values:
|
// Version information for an assembly consists of the following four values:
|
||||||
//
|
//
|
||||||
// Major Version
|
// Major Version
|
||||||
// Minor Version
|
// Minor Version
|
||||||
// Build Number
|
// Build Number
|
||||||
// Revision
|
// Revision
|
||||||
//
|
//
|
||||||
// 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("2.0.3.1")]
|
[assembly: AssemblyVersion("2.0.4.6")]
|
||||||
[assembly: AssemblyFileVersion("2.0.3.1")]
|
[assembly: AssemblyFileVersion("2.0.4.6")]
|
||||||
|
|||||||
@@ -25,8 +25,10 @@
|
|||||||
using SteamAuth;
|
using SteamAuth;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using ArchiSteamFarm.JSON;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal sealed class Trading {
|
internal sealed class Trading {
|
||||||
@@ -50,23 +52,19 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
internal Trading(Bot bot) {
|
internal Trading(Bot bot) {
|
||||||
if (bot == null) {
|
if (bot == null) {
|
||||||
throw new ArgumentNullException("bot");
|
throw new ArgumentNullException(nameof(bot));
|
||||||
}
|
}
|
||||||
|
|
||||||
Bot = bot;
|
Bot = bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task CheckTrades() {
|
internal async Task CheckTrades() {
|
||||||
bool shouldRun = false;
|
|
||||||
lock (TradesSemaphore) {
|
lock (TradesSemaphore) {
|
||||||
if (ParsingTasks < 2) {
|
if (ParsingTasks >= 2) {
|
||||||
ParsingTasks++;
|
return;
|
||||||
shouldRun = true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!shouldRun) {
|
ParsingTasks++;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await TradesSemaphore.WaitAsync().ConfigureAwait(false);
|
await TradesSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
@@ -80,8 +78,8 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task ParseActiveTrades() {
|
private async Task ParseActiveTrades() {
|
||||||
List<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
|
HashSet<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
|
||||||
if (tradeOffers == null) {
|
if ((tradeOffers == null) || (tradeOffers.Count == 0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,43 +88,119 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
|
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
|
||||||
if (tradeOffer == null || tradeOffer.trade_offer_state != Steam.TradeOffer.ETradeOfferState.Active) {
|
if ((tradeOffer == null) || (tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong tradeID;
|
if (await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false)) {
|
||||||
if (!ulong.TryParse(tradeOffer.tradeofferid, out tradeID)) {
|
Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||||
return;
|
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
|
||||||
}
|
|
||||||
|
|
||||||
if (ShouldAcceptTrade(tradeOffer)) {
|
|
||||||
Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
|
|
||||||
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
|
|
||||||
} else {
|
} else {
|
||||||
Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName);
|
Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ShouldAcceptTrade(Steam.TradeOffer tradeOffer) {
|
private async Task<bool> ShouldAcceptTrade(Steam.TradeOffer tradeOffer) {
|
||||||
if (tradeOffer == null) {
|
if (tradeOffer == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always accept trades when we're not losing anything
|
// Always accept trades when we're not losing anything
|
||||||
if (tradeOffer.items_to_give.Count == 0) {
|
if (tradeOffer.ItemsToGive.Count == 0) {
|
||||||
// Unless it's steam fuckup and we're dealing with broken trade
|
// Unless it's steam fuckup and we're dealing with broken trade
|
||||||
return tradeOffer.items_to_receive.Count > 0;
|
return tradeOffer.ItemsToReceive.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always accept trades from SteamMasterID
|
// Always accept trades from SteamMasterID
|
||||||
if (tradeOffer.OtherSteamID64 != 0 && tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID) {
|
if ((tradeOffer.OtherSteamID64 != 0) && (tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add optional SteamTradeMatcher integration here
|
// If we don't have SteamTradeMatcher enabled, this is the end for us
|
||||||
|
if (!Bot.BotConfig.SteamTradeMatcher) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If no rule above matched this trade, reject it
|
// Decline trade if we're giving more count-wise
|
||||||
return false;
|
if (tradeOffer.ItemsToGive.Count > tradeOffer.ItemsToReceive.Count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decline trade if we're losing anything but steam cards, or if it's non-dupes trade
|
||||||
|
if (!tradeOffer.IsSteamCardsOnlyTradeForUs() || !tradeOffer.IsPotentiallyDupesTradeForUs()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we're sure that STM trade is valid
|
||||||
|
// Now check if it's worth for us to do the trade
|
||||||
|
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false);
|
||||||
|
if ((inventory == null) || (inventory.Count == 0)) {
|
||||||
|
return true; // OK, assume that this trade is valid, we can't check our EQ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get appIDs we're interested in
|
||||||
|
HashSet<uint> appIDs = new HashSet<uint>();
|
||||||
|
foreach (Steam.Item item in tradeOffer.ItemsToGive) {
|
||||||
|
appIDs.Add(item.RealAppID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now remove from our inventory all items we're NOT interested in
|
||||||
|
inventory.RemoveWhere(item => !appIDs.Contains(item.RealAppID));
|
||||||
|
inventory.TrimExcess();
|
||||||
|
|
||||||
|
// If for some reason Valve is talking crap and we can't find mentioned items, assume OK
|
||||||
|
if (inventory.Count == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now let's create a map which maps items to their amount in our EQ
|
||||||
|
Dictionary<Tuple<ulong, ulong>, uint> amountMap = new Dictionary<Tuple<ulong, ulong>, uint>();
|
||||||
|
foreach (Steam.Item item in inventory) {
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(item.ClassID, item.InstanceID);
|
||||||
|
|
||||||
|
uint amount;
|
||||||
|
if (amountMap.TryGetValue(key, out amount)) {
|
||||||
|
amountMap[key] = amount + item.Amount;
|
||||||
|
} else {
|
||||||
|
amountMap[key] = item.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate our value of items to give
|
||||||
|
List<uint> amountsToGive = new List<uint>(tradeOffer.ItemsToGive.Count);
|
||||||
|
foreach (Tuple<ulong, ulong> key in tradeOffer.ItemsToGive.Select(item => new Tuple<ulong, ulong>(item.ClassID, item.InstanceID))) {
|
||||||
|
uint amount;
|
||||||
|
if (!amountMap.TryGetValue(key, out amount)) {
|
||||||
|
amountsToGive.Add(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
amountsToGive.Add(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort it ascending
|
||||||
|
amountsToGive.Sort();
|
||||||
|
|
||||||
|
// Calculate our value of items to receive
|
||||||
|
List<uint> amountsToReceive = new List<uint>(tradeOffer.ItemsToReceive.Count);
|
||||||
|
foreach (Tuple<ulong, ulong> key in tradeOffer.ItemsToReceive.Select(item => new Tuple<ulong, ulong>(item.ClassID, item.InstanceID))) {
|
||||||
|
uint amount;
|
||||||
|
if (!amountMap.TryGetValue(key, out amount)) {
|
||||||
|
amountsToReceive.Add(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
amountsToReceive.Add(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort it ascending
|
||||||
|
amountsToReceive.Sort();
|
||||||
|
|
||||||
|
// Check actual difference
|
||||||
|
int difference = amountsToGive.Select((t, i) => (int) (t - amountsToReceive[i])).Sum();
|
||||||
|
|
||||||
|
// Trade is worth for us if the difference is greater than 0
|
||||||
|
return difference > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,60 +24,27 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal static class Utilities {
|
internal static class Utilities {
|
||||||
|
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
|
||||||
internal static void Forget(this Task task) { }
|
internal static void Forget(this Task task) { }
|
||||||
|
|
||||||
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
|
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) => action == null ? Task.FromResult(true) : Task.WhenAll(sequence.Select(action));
|
||||||
return Task.WhenAll(sequence.Select(action));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static string GetCookieValue(this CookieContainer cookieContainer, string URL, string name) {
|
internal static string GetCookieValue(this CookieContainer cookieContainer, string url, string name) {
|
||||||
if (string.IsNullOrEmpty(URL) || string.IsNullOrEmpty(name)) {
|
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(name)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
CookieCollection cookies = cookieContainer.GetCookies(new Uri(URL));
|
CookieCollection cookies = cookieContainer.GetCookies(new Uri(url));
|
||||||
if (cookies == null || cookies.Count == 0) {
|
return cookies.Count == 0 ? null : (from Cookie cookie in cookies where cookie.Name.Equals(name, StringComparison.Ordinal) select cookie.Value).FirstOrDefault();
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Cookie cookie in cookies) {
|
|
||||||
if (!cookie.Name.Equals(name, StringComparison.Ordinal)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cookie.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Task SleepAsync(int miliseconds) {
|
internal static Task SleepAsync(int miliseconds) => miliseconds < 0 ? Task.FromResult(true) : Task.Delay(miliseconds);
|
||||||
if (miliseconds < 0) {
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.Delay(miliseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static uint GetCharCountInString(string s, char c) {
|
|
||||||
if (string.IsNullOrEmpty(s)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint count = 0;
|
|
||||||
foreach (char singleChar in s) {
|
|
||||||
if (singleChar == c) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,9 +52,7 @@ namespace ArchiSteamFarm {
|
|||||||
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
|
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool IsServerRunning() {
|
internal bool IsServerRunning() => ServiceHost != null;
|
||||||
return ServiceHost != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void StartServer() {
|
internal void StartServer() {
|
||||||
if (ServiceHost != null) {
|
if (ServiceHost != null) {
|
||||||
@@ -101,28 +99,16 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] args = input.Split(' ');
|
Bot bot = Bot.Bots.Values.FirstOrDefault();
|
||||||
|
if (bot == null) {
|
||||||
string botName;
|
return "ERROR: No bots are enabled!";
|
||||||
|
|
||||||
if (args.Length > 1) { // If we have args[1] provided, use given botName
|
|
||||||
botName = args[1];
|
|
||||||
} else { // If not, just pick first one
|
|
||||||
botName = Bot.Bots.Keys.FirstOrDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(botName)) {
|
if (Program.GlobalConfig.SteamOwnerID == 0) {
|
||||||
return "ERROR: Invalid botName: " + botName;
|
return "Refusing to handle request because SteamOwnerID is not set!";
|
||||||
}
|
}
|
||||||
|
|
||||||
Bot bot;
|
string command = "!" + input;
|
||||||
if (!Bot.Bots.TryGetValue(botName, out bot)) {
|
|
||||||
return "ERROR: Couldn't find any bot named: " + botName;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logging.LogGenericInfo("Received command: " + input);
|
|
||||||
|
|
||||||
string command = '!' + input;
|
|
||||||
string output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
|
string output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
|
||||||
|
|
||||||
Logging.LogGenericInfo("Answered to command: " + input + " with: " + output);
|
Logging.LogGenericInfo("Answered to command: " + input + " with: " + output);
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ using Newtonsoft.Json;
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -65,7 +64,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
internal WebBrowser(string identifier) {
|
internal WebBrowser(string identifier) {
|
||||||
if (string.IsNullOrEmpty(identifier)) {
|
if (string.IsNullOrEmpty(identifier)) {
|
||||||
throw new ArgumentNullException("identifier");
|
throw new ArgumentNullException(nameof(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
Identifier = identifier;
|
Identifier = identifier;
|
||||||
@@ -83,23 +82,23 @@ namespace ArchiSteamFarm {
|
|||||||
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent);
|
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> UrlGet(string request, string referer = null) {
|
internal async Task<bool> UrlHead(string request, string referer = null) {
|
||||||
if (string.IsNullOrEmpty(request)) {
|
if (string.IsNullOrEmpty(request)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (HttpResponseMessage response = await UrlGetToResponse(request, referer).ConfigureAwait(false)) {
|
using (HttpResponseMessage response = await UrlHeadToResponse(request, referer).ConfigureAwait(false)) {
|
||||||
return response != null;
|
return response != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> UrlPost(string request, Dictionary<string, string> data = null, string referer = null) {
|
internal async Task<Uri> UrlHeadToUri(string request, string referer = null) {
|
||||||
if (string.IsNullOrEmpty(request)) {
|
if (string.IsNullOrEmpty(request)) {
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (HttpResponseMessage response = await UrlPostToResponse(request, data, referer).ConfigureAwait(false)) {
|
using (HttpResponseMessage response = await UrlHeadToResponse(request, referer).ConfigureAwait(false)) {
|
||||||
return response != null;
|
return response == null ? null : response.RequestMessage.RequestUri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,10 +140,8 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
content = WebUtility.HtmlDecode(content);
|
|
||||||
HtmlDocument htmlDocument = new HtmlDocument();
|
HtmlDocument htmlDocument = new HtmlDocument();
|
||||||
htmlDocument.LoadHtml(content);
|
htmlDocument.LoadHtml(WebUtility.HtmlDecode(content));
|
||||||
|
|
||||||
return htmlDocument;
|
return htmlDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +189,16 @@ namespace ArchiSteamFarm {
|
|||||||
return xmlDocument;
|
return xmlDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task<bool> UrlPost(string request, Dictionary<string, string> data = null, string referer = null) {
|
||||||
|
if (string.IsNullOrEmpty(request)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (HttpResponseMessage response = await UrlPostToResponse(request, data, referer).ConfigureAwait(false)) {
|
||||||
|
return response != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<HttpResponseMessage> UrlGetToResponse(string request, string referer = null) {
|
private async Task<HttpResponseMessage> UrlGetToResponse(string request, string referer = null) {
|
||||||
if (string.IsNullOrEmpty(request)) {
|
if (string.IsNullOrEmpty(request)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -200,6 +207,14 @@ namespace ArchiSteamFarm {
|
|||||||
return await UrlRequest(request, HttpMethod.Get, null, referer).ConfigureAwait(false);
|
return await UrlRequest(request, HttpMethod.Get, null, referer).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<HttpResponseMessage> UrlHeadToResponse(string request, string referer = null) {
|
||||||
|
if (string.IsNullOrEmpty(request)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await UrlRequest(request, HttpMethod.Head, null, referer).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<HttpResponseMessage> UrlPostToResponse(string request, Dictionary<string, string> data = null, string referer = null) {
|
private async Task<HttpResponseMessage> UrlPostToResponse(string request, Dictionary<string, string> data = null, string referer = null) {
|
||||||
if (string.IsNullOrEmpty(request)) {
|
if (string.IsNullOrEmpty(request)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -209,17 +224,17 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, string referer = null) {
|
private async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, string referer = null) {
|
||||||
if (string.IsNullOrEmpty(request) || httpMethod == null) {
|
if (string.IsNullOrEmpty(request) || (httpMethod == null)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.StartsWith("https://") && Program.GlobalConfig.ForceHttp) {
|
if (request.StartsWith("https://", StringComparison.Ordinal) && Program.GlobalConfig.ForceHttp) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponseMessage responseMessage;
|
HttpResponseMessage responseMessage;
|
||||||
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
|
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
|
||||||
if (data != null && data.Count > 0) {
|
if ((data != null) && (data.Count > 0)) {
|
||||||
try {
|
try {
|
||||||
requestMessage.Content = new FormUrlEncodedContent(data);
|
requestMessage.Content = new FormUrlEncodedContent(data);
|
||||||
} catch (UriFormatException e) {
|
} catch (UriFormatException e) {
|
||||||
@@ -239,19 +254,22 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseMessage == null || !responseMessage.IsSuccessStatusCode) {
|
if (responseMessage == null) {
|
||||||
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
|
||||||
Logging.LogGenericError("Request: " + request + " failed!", Identifier);
|
|
||||||
if (responseMessage != null) {
|
|
||||||
Logging.LogGenericError("Status code: " + responseMessage.StatusCode, Identifier);
|
|
||||||
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false), Identifier);
|
|
||||||
responseMessage.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return responseMessage;
|
if (responseMessage.IsSuccessStatusCode) {
|
||||||
|
return responseMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
||||||
|
Logging.LogGenericError("Request: " + request + " failed!", Identifier);
|
||||||
|
Logging.LogGenericError("Status code: " + responseMessage.StatusCode, Identifier);
|
||||||
|
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false), Identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseMessage.Dispose();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Debug": false,
|
"Debug": false,
|
||||||
"Headless": false,
|
"Headless": false,
|
||||||
"AutoUpdates": true,
|
"AutoUpdates": true,
|
||||||
|
"AutoRestart": true,
|
||||||
"UpdateChannel": 1,
|
"UpdateChannel": 1,
|
||||||
"SteamProtocol": 6,
|
"SteamProtocol": 6,
|
||||||
"SteamOwnerID": 0,
|
"SteamOwnerID": 0,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"FarmOffline": false,
|
"FarmOffline": false,
|
||||||
"HandleOfflineMessages": false,
|
"HandleOfflineMessages": false,
|
||||||
"AcceptGifts": false,
|
"AcceptGifts": false,
|
||||||
|
"SteamTradeMatcher": false,
|
||||||
"ForwardKeysToOtherBots": false,
|
"ForwardKeysToOtherBots": false,
|
||||||
"DistributeKeys": false,
|
"DistributeKeys": false,
|
||||||
"UseAsfAsMobileAuthenticator": false,
|
"UseAsfAsMobileAuthenticator": false,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace ConfigGenerator {
|
namespace ConfigGenerator {
|
||||||
internal class ASFConfig {
|
internal abstract class ASFConfig {
|
||||||
internal static readonly HashSet<ASFConfig> ASFConfigs = new HashSet<ASFConfig>();
|
internal static readonly HashSet<ASFConfig> ASFConfigs = new HashSet<ASFConfig>();
|
||||||
|
|
||||||
internal string FilePath { get; set; }
|
internal string FilePath { get; set; }
|
||||||
@@ -39,13 +39,13 @@ namespace ConfigGenerator {
|
|||||||
|
|
||||||
protected ASFConfig(string filePath) : this() {
|
protected ASFConfig(string filePath) : this() {
|
||||||
if (string.IsNullOrEmpty(filePath)) {
|
if (string.IsNullOrEmpty(filePath)) {
|
||||||
throw new ArgumentNullException("filePath");
|
throw new ArgumentNullException(nameof(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePath = filePath;
|
FilePath = filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal virtual void Save() {
|
internal void Save() {
|
||||||
lock (FilePath) {
|
lock (FilePath) {
|
||||||
try {
|
try {
|
||||||
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
|
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
|
||||||
@@ -55,7 +55,7 @@ namespace ConfigGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal virtual void Remove() {
|
internal void Remove() {
|
||||||
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
||||||
lock (FilePath) {
|
lock (FilePath) {
|
||||||
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
||||||
@@ -66,10 +66,11 @@ namespace ConfigGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ASFConfigs.Remove(this);
|
ASFConfigs.Remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal virtual void Rename(string botName) {
|
internal void Rename(string botName) {
|
||||||
if (string.IsNullOrEmpty(botName)) {
|
if (string.IsNullOrEmpty(botName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -83,6 +84,7 @@ namespace ConfigGenerator {
|
|||||||
Logging.LogGenericException(e);
|
Logging.LogGenericException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePath = Path.Combine(Program.ConfigDirectory, botName + ".json");
|
FilePath = Path.Combine(Program.ConfigDirectory, botName + ".json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,12 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace ConfigGenerator {
|
namespace ConfigGenerator {
|
||||||
|
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
internal sealed class BotConfig : ASFConfig {
|
internal sealed class BotConfig : ASFConfig {
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool Enabled { get; set; } = false;
|
public bool Enabled { get; set; } = false;
|
||||||
@@ -38,7 +41,7 @@ namespace ConfigGenerator {
|
|||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
public string SteamLogin { get; set; } = null;
|
public string SteamLogin { get; set; } = null;
|
||||||
|
|
||||||
[JsonProperty]
|
[JsonProperty, PasswordPropertyText(true)]
|
||||||
public string SteamPassword { get; set; } = null;
|
public string SteamPassword { get; set; } = null;
|
||||||
|
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
@@ -68,6 +71,9 @@ namespace ConfigGenerator {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool AcceptGifts { get; set; } = false;
|
public bool AcceptGifts { get; set; } = false;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
public bool SteamTradeMatcher { get; set; } = false;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool ForwardKeysToOtherBots { get; set; } = false;
|
public bool ForwardKeysToOtherBots { get; set; } = false;
|
||||||
|
|
||||||
@@ -124,12 +130,12 @@ namespace ConfigGenerator {
|
|||||||
return botConfig;
|
return botConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This constructor is used only by deserializer
|
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
private BotConfig() { }
|
private BotConfig() { }
|
||||||
|
|
||||||
private BotConfig(string filePath) : base(filePath) {
|
private BotConfig(string filePath) : base(filePath) {
|
||||||
if (string.IsNullOrEmpty(filePath)) {
|
if (string.IsNullOrEmpty(filePath)) {
|
||||||
throw new ArgumentNullException("filePath");
|
throw new ArgumentNullException(nameof(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
GamesPlayedWhileIdle.Add(0);
|
GamesPlayedWhileIdle.Add(0);
|
||||||
|
|||||||
@@ -83,6 +83,7 @@
|
|||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
</Compile>
|
</Compile>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
<None Include="Properties\Settings.settings">
|
<None Include="Properties\Settings.settings">
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ using System.IO;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace ConfigGenerator {
|
namespace ConfigGenerator {
|
||||||
internal class ConfigPage : TabPage {
|
internal sealed class ConfigPage : TabPage {
|
||||||
internal readonly ASFConfig ASFConfig;
|
internal readonly ASFConfig ASFConfig;
|
||||||
|
|
||||||
internal ConfigPage(ASFConfig config) {
|
internal ConfigPage(ASFConfig config) {
|
||||||
@@ -42,8 +42,6 @@ namespace ConfigGenerator {
|
|||||||
Controls.Add(enhancedPropertyGrid);
|
Controls.Add(enhancedPropertyGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RefreshText() {
|
internal void RefreshText() => Text = Path.GetFileNameWithoutExtension(ASFConfig.FilePath);
|
||||||
Text = Path.GetFileNameWithoutExtension(ASFConfig.FilePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,14 +22,16 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace ConfigGenerator {
|
namespace ConfigGenerator {
|
||||||
internal static class Debugging {
|
internal static class Debugging {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
|
||||||
internal static readonly bool IsDebugBuild = true;
|
internal static readonly bool IsDebugBuild = true;
|
||||||
#else
|
#else
|
||||||
|
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
|
||||||
internal static readonly bool IsDebugBuild = false;
|
internal static readonly bool IsDebugBuild = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
internal static bool IsReleaseBuild => !IsDebugBuild;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,92 +25,95 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using ConfigGenerator.Properties;
|
||||||
|
|
||||||
namespace ConfigGenerator {
|
namespace ConfigGenerator {
|
||||||
class DialogBox {
|
internal static class DialogBox {
|
||||||
public static DialogResult InputBox(string title, string promptText, out string value) {
|
internal static DialogResult InputBox(string title, string promptText, out string value) {
|
||||||
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
|
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
|
||||||
value = null;
|
value = null;
|
||||||
return DialogResult.Abort;
|
return DialogResult.Abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
Form form = new Form();
|
TextBox textBox = new TextBox {
|
||||||
Label label = new Label();
|
Anchor = AnchorStyles.Right,
|
||||||
TextBox textBox = new TextBox();
|
Bounds = new Rectangle(12, 36, 372, 20),
|
||||||
|
Width = 1000
|
||||||
|
};
|
||||||
|
|
||||||
textBox.Width = 1000;
|
Button buttonOk = new Button {
|
||||||
Button buttonOk = new Button();
|
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||||
Button buttonCancel = new Button();
|
Bounds = new Rectangle(228, 72, 75, 23),
|
||||||
|
DialogResult = DialogResult.OK,
|
||||||
|
Text = Resources.OK
|
||||||
|
};
|
||||||
|
|
||||||
form.Text = title;
|
Button buttonCancel = new Button {
|
||||||
label.Text = promptText;
|
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||||
|
Bounds = new Rectangle(309, 72, 75, 23),
|
||||||
|
DialogResult = DialogResult.Cancel,
|
||||||
|
Text = Resources.Cancel
|
||||||
|
};
|
||||||
|
|
||||||
buttonOk.Text = "OK";
|
Label label = new Label {
|
||||||
buttonCancel.Text = "Cancel";
|
AutoSize = true,
|
||||||
buttonOk.DialogResult = DialogResult.OK;
|
Bounds = new Rectangle(9, 20, 372, 13),
|
||||||
buttonCancel.DialogResult = DialogResult.Cancel;
|
Text = promptText
|
||||||
|
};
|
||||||
|
|
||||||
label.SetBounds(9, 20, 372, 13);
|
Form form = new Form {
|
||||||
textBox.SetBounds(12, 36, 372, 20);
|
AcceptButton = buttonOk,
|
||||||
buttonOk.SetBounds(228, 72, 75, 23);
|
CancelButton = buttonCancel,
|
||||||
buttonCancel.SetBounds(309, 72, 75, 23);
|
ClientSize = new Size(Math.Max(300, label.Right + 10), 107),
|
||||||
|
Controls = { label, textBox, buttonOk, buttonCancel },
|
||||||
label.AutoSize = true;
|
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||||
textBox.Anchor = textBox.Anchor | AnchorStyles.Right;
|
MinimizeBox = false,
|
||||||
buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
MaximizeBox = false,
|
||||||
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
StartPosition = FormStartPosition.CenterScreen,
|
||||||
|
Text = title
|
||||||
form.ClientSize = new Size(396, 107);
|
};
|
||||||
form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel });
|
|
||||||
form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
|
|
||||||
form.FormBorderStyle = FormBorderStyle.FixedDialog;
|
|
||||||
form.StartPosition = FormStartPosition.CenterScreen;
|
|
||||||
form.MinimizeBox = false;
|
|
||||||
form.MaximizeBox = false;
|
|
||||||
form.AcceptButton = buttonOk;
|
|
||||||
form.CancelButton = buttonCancel;
|
|
||||||
|
|
||||||
DialogResult dialogResult = form.ShowDialog();
|
DialogResult dialogResult = form.ShowDialog();
|
||||||
value = textBox.Text;
|
value = textBox.Text;
|
||||||
return dialogResult;
|
return dialogResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DialogResult YesNoBox(string title, string promptText) {
|
internal static DialogResult YesNoBox(string title, string promptText) {
|
||||||
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
|
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
|
||||||
return DialogResult.Abort;
|
return DialogResult.Abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
Form form = new Form();
|
Button buttonYes = new Button {
|
||||||
Label label = new Label();
|
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||||
|
Bounds = new Rectangle(228, 72, 75, 23),
|
||||||
|
DialogResult = DialogResult.Yes,
|
||||||
|
Text = Resources.Yes
|
||||||
|
};
|
||||||
|
|
||||||
Button buttonOk = new Button();
|
Button buttonNo = new Button {
|
||||||
Button buttonCancel = new Button();
|
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||||
|
Bounds = new Rectangle(309, 72, 75, 23),
|
||||||
|
DialogResult = DialogResult.No,
|
||||||
|
Text = Resources.No
|
||||||
|
};
|
||||||
|
|
||||||
form.Text = title;
|
Label label = new Label {
|
||||||
label.Text = promptText;
|
AutoSize = true,
|
||||||
|
Bounds = new Rectangle(9, 20, 372, 13),
|
||||||
|
Text = promptText
|
||||||
|
};
|
||||||
|
|
||||||
buttonOk.Text = "Yes";
|
Form form = new Form {
|
||||||
buttonCancel.Text = "No";
|
AcceptButton = buttonYes,
|
||||||
buttonOk.DialogResult = DialogResult.Yes;
|
CancelButton = buttonNo,
|
||||||
buttonCancel.DialogResult = DialogResult.No;
|
ClientSize = new Size(Math.Max(300, label.Right + 10), 107),
|
||||||
|
Controls = { label, buttonYes, buttonNo },
|
||||||
label.SetBounds(9, 20, 372, 13);
|
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||||
buttonOk.SetBounds(228, 50, 75, 23);
|
MinimizeBox = false,
|
||||||
buttonCancel.SetBounds(309, 50, 75, 23);
|
MaximizeBox = false,
|
||||||
|
StartPosition = FormStartPosition.CenterScreen,
|
||||||
label.AutoSize = true;
|
Text = title
|
||||||
buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
};
|
||||||
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
|
||||||
|
|
||||||
form.ClientSize = new Size(396, 80);
|
|
||||||
form.Controls.AddRange(new Control[] { label, buttonOk, buttonCancel });
|
|
||||||
form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
|
|
||||||
form.FormBorderStyle = FormBorderStyle.FixedDialog;
|
|
||||||
form.StartPosition = FormStartPosition.CenterScreen;
|
|
||||||
form.MinimizeBox = false;
|
|
||||||
form.MaximizeBox = false;
|
|
||||||
form.AcceptButton = buttonOk;
|
|
||||||
form.CancelButton = buttonCancel;
|
|
||||||
|
|
||||||
DialogResult dialogResult = form.ShowDialog();
|
DialogResult dialogResult = form.ShowDialog();
|
||||||
return dialogResult;
|
return dialogResult;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace ConfigGenerator {
|
|||||||
|
|
||||||
internal EnhancedPropertyGrid(ASFConfig config) {
|
internal EnhancedPropertyGrid(ASFConfig config) {
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
throw new ArgumentNullException("config");
|
throw new ArgumentNullException(nameof(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASFConfig = config;
|
ASFConfig = config;
|
||||||
@@ -53,20 +53,24 @@ namespace ConfigGenerator {
|
|||||||
|
|
||||||
BotConfig botConfig = ASFConfig as BotConfig;
|
BotConfig botConfig = ASFConfig as BotConfig;
|
||||||
if (botConfig != null) {
|
if (botConfig != null) {
|
||||||
if (botConfig.Enabled) {
|
if (!botConfig.Enabled) {
|
||||||
Tutorial.OnAction(Tutorial.EPhase.BotEnabled);
|
return;
|
||||||
if (!string.IsNullOrEmpty(botConfig.SteamLogin) && !string.IsNullOrEmpty(botConfig.SteamPassword)) {
|
}
|
||||||
Tutorial.OnAction(Tutorial.EPhase.BotReady);
|
|
||||||
}
|
Tutorial.OnAction(Tutorial.EPhase.BotEnabled);
|
||||||
|
if (!string.IsNullOrEmpty(botConfig.SteamLogin) && !string.IsNullOrEmpty(botConfig.SteamPassword)) {
|
||||||
|
Tutorial.OnAction(Tutorial.EPhase.BotReady);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalConfig globalConfig = ASFConfig as GlobalConfig;
|
GlobalConfig globalConfig = ASFConfig as GlobalConfig;
|
||||||
if (globalConfig != null) {
|
if (globalConfig == null) {
|
||||||
if (globalConfig.SteamOwnerID != 0) {
|
return;
|
||||||
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigReady);
|
}
|
||||||
}
|
|
||||||
|
if (globalConfig.SteamOwnerID != 0) {
|
||||||
|
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigReady);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,14 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
|
||||||
namespace ConfigGenerator {
|
namespace ConfigGenerator {
|
||||||
|
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
internal sealed class GlobalConfig : ASFConfig {
|
internal sealed class GlobalConfig : ASFConfig {
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
internal enum EUpdateChannel : byte {
|
internal enum EUpdateChannel : byte {
|
||||||
Unknown,
|
Unknown,
|
||||||
Stable,
|
Stable,
|
||||||
@@ -43,7 +46,7 @@ namespace ConfigGenerator {
|
|||||||
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
|
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
|
||||||
|
|
||||||
// This is hardcoded blacklist which should not be possible to change
|
// 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 };
|
private static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280 };
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool Debug { get; set; } = false;
|
public bool Debug { get; set; } = false;
|
||||||
@@ -54,6 +57,9 @@ namespace ConfigGenerator {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool AutoUpdates { get; set; } = true;
|
public bool AutoUpdates { get; set; } = true;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
public bool AutoRestart { get; set; } = true;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public EUpdateChannel UpdateChannel { get; set; } = EUpdateChannel.Stable;
|
public EUpdateChannel UpdateChannel { get; set; } = EUpdateChannel.Stable;
|
||||||
|
|
||||||
@@ -99,7 +105,6 @@ namespace ConfigGenerator {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool Statistics { get; set; } = true;
|
public bool Statistics { get; set; } = true;
|
||||||
|
|
||||||
// TODO: Please remove me immediately after https://github.com/SteamRE/SteamKit/issues/254 gets fixed
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool HackIgnoreMachineID { get; set; } = false;
|
public bool HackIgnoreMachineID { get; set; } = false;
|
||||||
|
|
||||||
@@ -158,20 +163,22 @@ namespace ConfigGenerator {
|
|||||||
globalConfig.HttpTimeout = DefaultHttpTimeout;
|
globalConfig.HttpTimeout = DefaultHttpTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalConfig.WCFPort == 0) {
|
if (globalConfig.WCFPort != 0) {
|
||||||
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
|
return globalConfig;
|
||||||
globalConfig.WCFPort = DefaultWCFPort;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
|
||||||
|
globalConfig.WCFPort = DefaultWCFPort;
|
||||||
|
|
||||||
return globalConfig;
|
return globalConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This constructor is used only by deserializer
|
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||||
private GlobalConfig() { }
|
private GlobalConfig() { }
|
||||||
|
|
||||||
private GlobalConfig(string filePath) : base(filePath) {
|
private GlobalConfig(string filePath) : base(filePath) {
|
||||||
if (string.IsNullOrEmpty(filePath)) {
|
if (string.IsNullOrEmpty(filePath)) {
|
||||||
throw new ArgumentNullException("filePath");
|
throw new ArgumentNullException(nameof(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
Blacklist.AddRange(GlobalBlacklist);
|
Blacklist.AddRange(GlobalBlacklist);
|
||||||
|
|||||||
@@ -23,9 +23,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using ConfigGenerator.Properties;
|
||||||
|
|
||||||
namespace ConfigGenerator {
|
namespace ConfigGenerator {
|
||||||
internal static class Logging {
|
internal static class Logging {
|
||||||
@@ -34,60 +34,40 @@ namespace ConfigGenerator {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageBox.Show(message, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
MessageBox.Show(message, Resources.Information, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LogGenericWTF(string message, [CallerMemberName] string previousMethodName = "") {
|
internal static void LogGenericError(string message, [CallerMemberName] string previousMethodName = null) {
|
||||||
if (string.IsNullOrEmpty(message)) {
|
if (string.IsNullOrEmpty(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageBox.Show(previousMethodName + "() " + message, "WTF", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show(previousMethodName + @"() " + message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LogGenericError(string message, [CallerMemberName] string previousMethodName = "") {
|
internal static void LogGenericException(Exception exception, [CallerMemberName] string previousMethodName = null) {
|
||||||
|
while (true) {
|
||||||
|
if (exception == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBox.Show(previousMethodName + @"() " + exception.Message + Environment.NewLine + exception.StackTrace, Resources.Exception, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
|
||||||
|
if (exception.InnerException != null) {
|
||||||
|
exception = exception.InnerException;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void LogGenericWarning(string message, [CallerMemberName] string previousMethodName = null) {
|
||||||
if (string.IsNullOrEmpty(message)) {
|
if (string.IsNullOrEmpty(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageBox.Show(previousMethodName + "() " + message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show(previousMethodName + @"() " + message, Resources.Warning, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
}
|
|
||||||
|
|
||||||
internal static void LogGenericException(Exception exception, [CallerMemberName] string previousMethodName = "") {
|
|
||||||
if (exception == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageBox.Show(previousMethodName + "() " + exception.Message + Environment.NewLine + exception.StackTrace, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
||||||
|
|
||||||
if (exception.InnerException != null) {
|
|
||||||
LogGenericException(exception.InnerException, previousMethodName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void LogGenericWarning(string message, [CallerMemberName] string previousMethodName = "") {
|
|
||||||
if (string.IsNullOrEmpty(message)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageBox.Show(previousMethodName + "() " + message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void LogNullError(string nullObjectName, [CallerMemberName] string previousMethodName = "") {
|
|
||||||
if (string.IsNullOrEmpty(nullObjectName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogGenericError(nullObjectName + " is null!", previousMethodName);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Conditional("DEBUG")]
|
|
||||||
internal static void LogGenericDebug(string message, [CallerMemberName] string previousMethodName = "") {
|
|
||||||
if (string.IsNullOrEmpty(message)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageBox.Show(previousMethodName + "() " + message, "Debug", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,26 +26,27 @@ using System;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace ConfigGenerator {
|
namespace ConfigGenerator {
|
||||||
public partial class MainForm : Form {
|
internal sealed partial class MainForm : Form {
|
||||||
private const byte ReservedTabs = 3;
|
private const byte ReservedTabs = 3;
|
||||||
|
|
||||||
private readonly TabPage NewTab = new TabPage { Text = "+" };
|
private readonly TabPage NewTab = new TabPage { Text = @"+" };
|
||||||
private readonly TabPage RemoveTab = new TabPage { Text = "-" };
|
private readonly TabPage RemoveTab = new TabPage { Text = @"-" };
|
||||||
private readonly TabPage RenameTab = new TabPage { Text = "~" };
|
private readonly TabPage RenameTab = new TabPage { Text = @"~" };
|
||||||
|
|
||||||
private ConfigPage ASFTab;
|
private ConfigPage ASFTab;
|
||||||
private TabPage OldTab;
|
private TabPage OldTab;
|
||||||
|
|
||||||
public MainForm() {
|
internal MainForm() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MainForm_Load(object sender, EventArgs e) {
|
private void MainForm_Load(object sender, EventArgs e) {
|
||||||
if (sender == null || e == null) {
|
if ((sender == null) || (e == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ namespace ConfigGenerator {
|
|||||||
|
|
||||||
MainTab.TabPages.Add(ASFTab);
|
MainTab.TabPages.Add(ASFTab);
|
||||||
|
|
||||||
foreach (var configFile in Directory.EnumerateFiles(Program.ConfigDirectory, "*.json")) {
|
foreach (string configFile in Directory.EnumerateFiles(Program.ConfigDirectory, "*.json")) {
|
||||||
string botName = Path.GetFileNameWithoutExtension(configFile);
|
string botName = Path.GetFileNameWithoutExtension(configFile);
|
||||||
switch (botName) {
|
switch (botName) {
|
||||||
case Program.ASF:
|
case Program.ASF:
|
||||||
@@ -71,7 +72,7 @@ namespace ConfigGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void MainTab_Selected(object sender, TabControlEventArgs e) {
|
private void MainTab_Selected(object sender, TabControlEventArgs e) {
|
||||||
if (sender == null || e == null) {
|
if ((sender == null) || (e == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,11 +152,9 @@ namespace ConfigGenerator {
|
|||||||
// Get rid of any potential whitespaces in bot name
|
// Get rid of any potential whitespaces in bot name
|
||||||
input = Regex.Replace(input, @"\s+", "");
|
input = Regex.Replace(input, @"\s+", "");
|
||||||
|
|
||||||
foreach (ASFConfig config in ASFConfig.ASFConfigs) {
|
if (ASFConfig.ASFConfigs.Select(config => Path.GetFileNameWithoutExtension(config.FilePath)).Any(fileNameWithoutExtension => (fileNameWithoutExtension == null) || fileNameWithoutExtension.Equals(input))) {
|
||||||
if (Path.GetFileNameWithoutExtension(config.FilePath).Equals(input)) {
|
Logging.LogGenericError("Bot with such name exists already!");
|
||||||
Logging.LogGenericError("Bot with such name exists already!");
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input = Path.Combine(Program.ConfigDirectory, input + ".json");
|
input = Path.Combine(Program.ConfigDirectory, input + ".json");
|
||||||
@@ -170,7 +169,7 @@ namespace ConfigGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void MainTab_Deselecting(object sender, TabControlCancelEventArgs e) {
|
private void MainTab_Deselecting(object sender, TabControlCancelEventArgs e) {
|
||||||
if (sender == null || e == null) {
|
if ((sender == null) || (e == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +177,7 @@ namespace ConfigGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void MainForm_Shown(object sender, EventArgs e) {
|
private void MainForm_Shown(object sender, EventArgs e) {
|
||||||
if (sender == null || e == null) {
|
if ((sender == null) || (e == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +185,7 @@ namespace ConfigGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void MainForm_HelpButtonClicked(object sender, CancelEventArgs e) {
|
private void MainForm_HelpButtonClicked(object sender, CancelEventArgs e) {
|
||||||
if (sender == null || e == null) {
|
if ((sender == null) || (e == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace ConfigGenerator {
|
|||||||
|
|
||||||
private const string ASFDirectory = "ArchiSteamFarm";
|
private const string ASFDirectory = "ArchiSteamFarm";
|
||||||
|
|
||||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
private static readonly string ExecutableDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The main entry point for the application.
|
/// The main entry point for the application.
|
||||||
@@ -61,10 +61,12 @@ namespace ConfigGenerator {
|
|||||||
// Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up
|
// Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up
|
||||||
for (byte i = 0; i < 4; i++) {
|
for (byte i = 0; i < 4; i++) {
|
||||||
Directory.SetCurrentDirectory("..");
|
Directory.SetCurrentDirectory("..");
|
||||||
if (Directory.Exists(ASFDirectory)) {
|
if (!Directory.Exists(ASFDirectory)) {
|
||||||
Directory.SetCurrentDirectory(ASFDirectory);
|
continue;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Directory.SetCurrentDirectory(ASFDirectory);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If config directory doesn't exist after our adjustment, abort all of that
|
// If config directory doesn't exist after our adjustment, abort all of that
|
||||||
@@ -73,14 +75,16 @@ namespace ConfigGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Directory.Exists(ConfigDirectory)) {
|
if (Directory.Exists(ConfigDirectory)) {
|
||||||
Logging.LogGenericError("Config directory could not be found!");
|
return;
|
||||||
Environment.Exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logging.LogGenericError("Config directory could not be found!");
|
||||||
|
Environment.Exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
|
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
|
||||||
if (sender == null || args == null) {
|
if ((sender == null) || (args == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +92,7 @@ namespace ConfigGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
|
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
|
||||||
if (sender == null || args == null) {
|
if ((sender == null) || (args == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
// General Information about an assembly is controlled through the following
|
||||||
// set of attributes. Change these attribute values to modify the information
|
// set of attributes. Change these attribute values to modify the information
|
||||||
// associated with an assembly.
|
// associated with an assembly.
|
||||||
[assembly: AssemblyTitle("ConfigGenerator")]
|
[assembly: AssemblyTitle("ConfigGenerator")]
|
||||||
@@ -14,8 +13,8 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
// to COM components. If you need to access a type in this assembly from
|
// to COM components. If you need to access a type in this assembly from
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
@@ -25,11 +24,11 @@ using System.Runtime.InteropServices;
|
|||||||
// Version information for an assembly consists of the following four values:
|
// Version information for an assembly consists of the following four values:
|
||||||
//
|
//
|
||||||
// Major Version
|
// Major Version
|
||||||
// Minor Version
|
// Minor Version
|
||||||
// Build Number
|
// Build Number
|
||||||
// Revision
|
// Revision
|
||||||
//
|
//
|
||||||
// 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("1.0.0.0")]
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
|||||||
173
ConfigGenerator/Properties/Resources.Designer.cs
generated
173
ConfigGenerator/Properties/Resources.Designer.cs
generated
@@ -9,54 +9,127 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ConfigGenerator.Properties {
|
namespace ConfigGenerator.Properties {
|
||||||
|
using System;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
/// <summary>
|
||||||
/// </summary>
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
/// </summary>
|
||||||
// class via a tool like ResGen or Visual Studio.
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
// class via a tool like ResGen or Visual Studio.
|
||||||
// with the /str option, or rebuild your VS project.
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
// with the /str option, or rebuild your VS project.
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
internal class Resources {
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources {
|
||||||
private static global::System.Resources.ResourceManager resourceMan;
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
||||||
internal Resources() {
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
}
|
internal Resources() {
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Returns the cached ResourceManager instance used by this class.
|
/// <summary>
|
||||||
/// </summary>
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
/// </summary>
|
||||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
get {
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
if ((resourceMan == null)) {
|
get {
|
||||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ConfigGenerator.Properties.Resources", typeof(Resources).Assembly);
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
resourceMan = temp;
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ConfigGenerator.Properties.Resources", typeof(Resources).Assembly);
|
||||||
}
|
resourceMan = temp;
|
||||||
return resourceMan;
|
}
|
||||||
}
|
return resourceMan;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Overrides the current thread's CurrentUICulture property for all
|
/// <summary>
|
||||||
/// resource lookups using this strongly typed resource class.
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
/// </summary>
|
/// resource lookups using this strongly typed resource class.
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
/// </summary>
|
||||||
internal static global::System.Globalization.CultureInfo Culture {
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
get {
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
return resourceCulture;
|
get {
|
||||||
}
|
return resourceCulture;
|
||||||
set {
|
}
|
||||||
resourceCulture = value;
|
set {
|
||||||
}
|
resourceCulture = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Cancel.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Cancel {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Cancel", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Error.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Error {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Exception.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Exception {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Exception", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Information.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Information {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Information", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to No.
|
||||||
|
/// </summary>
|
||||||
|
internal static string No {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("No", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to OK.
|
||||||
|
/// </summary>
|
||||||
|
internal static string OK {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("OK", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Warning.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Warning {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Warning", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Yes.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Yes {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Yes", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,4 +114,28 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<data name="Information" xml:space="preserve">
|
||||||
|
<value>Information</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error" xml:space="preserve">
|
||||||
|
<value>Error</value>
|
||||||
|
</data>
|
||||||
|
<data name="Exception" xml:space="preserve">
|
||||||
|
<value>Exception</value>
|
||||||
|
</data>
|
||||||
|
<data name="Warning" xml:space="preserve">
|
||||||
|
<value>Warning</value>
|
||||||
|
</data>
|
||||||
|
<data name="OK" xml:space="preserve">
|
||||||
|
<value>OK</value>
|
||||||
|
</data>
|
||||||
|
<data name="Cancel" xml:space="preserve">
|
||||||
|
<value>Cancel</value>
|
||||||
|
</data>
|
||||||
|
<data name="Yes" xml:space="preserve">
|
||||||
|
<value>Yes</value>
|
||||||
|
</data>
|
||||||
|
<data name="No" xml:space="preserve">
|
||||||
|
<value>No</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -38,12 +38,12 @@ namespace ConfigGenerator {
|
|||||||
GlobalConfigReady
|
GlobalConfigReady
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool Enabled { get; set; } = true;
|
internal static bool Enabled { private get; set; } = true;
|
||||||
|
|
||||||
private static EPhase NextPhase = EPhase.Start;
|
private static EPhase NextPhase = EPhase.Start;
|
||||||
|
|
||||||
internal static void OnAction(EPhase phase) {
|
internal static void OnAction(EPhase phase) {
|
||||||
if (!Enabled || phase != NextPhase) {
|
if (!Enabled || (phase != NextPhase)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<!--
|
||||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||||
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||||
del "$(SolutionDir)out\ASF-GUI.exe.config"
|
del "$(SolutionDir)out\ASF-GUI.exe.config"
|
||||||
@@ -143,6 +144,7 @@
|
|||||||
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||||
rm "$(SolutionDir)out/ASF-GUI.exe.config"
|
rm "$(SolutionDir)out/ASF-GUI.exe.config"
|
||||||
</PostBuildEvent>
|
</PostBuildEvent>
|
||||||
|
-->
|
||||||
</PropertyGroup>
|
</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.
|
||||||
|
|||||||
9
appveyor.yml
Normal file
9
appveyor.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: 1.0.{build}-{branch}
|
||||||
|
image: Visual Studio 2015
|
||||||
|
configuration: Release
|
||||||
|
platform: Any CPU
|
||||||
|
clone_depth: 10
|
||||||
|
build:
|
||||||
|
project: ArchiSteamFarm.sln
|
||||||
|
parallel: true
|
||||||
|
verbosity: minimal
|
||||||
Reference in New Issue
Block a user