Compare commits

..

25 Commits

Author SHA1 Message Date
JustArchi
b60b864aca Misc 2016-05-13 19:43:48 +02:00
JustArchi
4c64141462 Misc 2016-05-13 19:40:55 +02:00
JustArchi
4b50596709 Enhance concurrent access with ArchiBoT concurrent hashset 2016-05-13 19:39:54 +02:00
JustArchi
622f060575 Prefer flags over comments 2016-05-13 19:20:24 +02:00
JustArchi
20038e8c86 Derp 2016-05-13 06:33:25 +02:00
JustArchi
b8faca2517 Gigantic code review (with ReSharper) 2016-05-13 06:32:42 +02:00
JustArchi
6f93139a18 Bump 2016-05-13 01:43:21 +02:00
JustArchi
50b5c7b87f Correct STM algorithm when overpaying with cards from the same game
us: X X Y
them: Y Y Z

trade:
Y -> X + Z

New:
toGive: 1
toReceive: 2 + 0 -> 0 + 2

diff: 1 - 0 = 1
1 > 0 ? True

Old:
toGive: 1
toReceive: 2

1 > 2 ? False
2016-05-13 01:35:17 +02:00
JustArchi
b60448ef4c Bump 2016-05-12 19:00:26 +02:00
JustArchi
fad08a1fa9 Fix linking new authenticator with null password 2016-05-12 16:32:04 +02:00
JustArchi
a180c100c6 Make use of CellID, closes #223 2016-05-08 15:52:57 +02:00
JustArchi
9d97ca16e8 Misc WCF code review 2016-05-07 15:24:09 +02:00
JustArchi
e833415718 Closes #221 2016-05-07 15:08:24 +02:00
JustArchi
6bb296a674 Bump 2016-05-06 23:58:50 +02:00
JustArchi
f1e5874868 Misc 2016-05-06 23:54:47 +02:00
JustArchi
38e2088d09 Misc 2016-05-06 23:48:33 +02:00
JustArchi
8447e07aa0 Allow overpaying also in steam cards 2016-05-06 23:38:22 +02:00
JustArchi
fbe4e4bc6d Allow STM overpaying 2016-05-06 23:31:00 +02:00
JustArchi
f1213607ce Don't react in any way to failed commands sent by non-owner 2016-05-06 18:07:03 +02:00
JustArchi
de832c530b Misc 2016-05-04 16:43:16 +02:00
JustArchi
0c872b17e2 Misc 2016-05-04 16:41:00 +02:00
JustArchi
5fcbb85b4c Bump 2016-05-03 15:54:26 +02:00
JustArchi
3cdc93d373 Cut SendOnFarmingFinished spam
Allow only one trade to be sent if we didn't farm anything
2016-05-03 15:37:11 +02:00
JustArchi
36e99d9139 Code review 2016-05-03 07:26:41 +02:00
JustArchi
7bee2d468b Bump 2016-04-29 16:52:57 +02:00
36 changed files with 1185 additions and 1022 deletions

View File

@@ -26,7 +26,9 @@ using SteamKit2;
using SteamKit2.Internal;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
@@ -36,7 +38,7 @@ namespace ArchiSteamFarm {
internal ArchiHandler(Bot bot) {
if (bot == null) {
throw new ArgumentNullException("bot");
throw new ArgumentNullException(nameof(bot));
}
Bot = bot;
@@ -52,67 +54,60 @@ namespace ArchiSteamFarm {
*/
internal sealed class NotificationsCallback : CallbackMsg {
internal sealed class Notification {
internal enum ENotificationType : uint {
Unknown = 0,
Trading = 1,
// Only custom below, different than ones available as user_notification_type
Items = 514
}
internal readonly ENotificationType NotificationType;
internal Notification(ENotificationType notificationType) {
NotificationType = notificationType;
}
internal enum ENotification : byte {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
Unknown = 0,
Trading = 1,
// Only custom below, different than ones available as user_notification_type
Items = 255
}
internal readonly HashSet<Notification> Notifications;
internal readonly HashSet<ENotification> Notifications;
internal NotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
JobID = jobID;
if (msg == null || msg.notifications == null) {
return;
if ((jobID == null) || (msg == null)) {
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
}
Notifications = new HashSet<Notification>();
foreach (var notification in msg.notifications) {
Notifications.Add(new Notification((Notification.ENotificationType) notification.user_notification_type));
JobID = jobID;
Notifications = new HashSet<ENotification>();
foreach (CMsgClientUserNotifications.Notification notification in msg.notifications) {
Notifications.Add((ENotification) notification.user_notification_type);
}
}
internal NotificationsCallback(JobID jobID, CMsgClientItemAnnouncements msg) {
JobID = jobID;
if (msg == null) {
return;
if ((jobID == null) || (msg == null)) {
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
}
JobID = jobID;
if (msg.count_new_items > 0) {
Notifications = new HashSet<Notification>() {
new Notification(Notification.ENotificationType.Items)
Notifications = new HashSet<ENotification> {
ENotification.Items
};
}
}
}
internal sealed class OfflineMessageCallback : CallbackMsg {
internal readonly uint OfflineMessages;
internal readonly uint OfflineMessagesCount;
internal OfflineMessageCallback(JobID jobID, CMsgClientOfflineMessageNotification msg) {
JobID = jobID;
if (msg == null) {
return;
if ((jobID == null) || (msg == null)) {
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
}
OfflineMessages = msg.offline_messages;
JobID = jobID;
OfflineMessagesCount = msg.offline_messages;
}
}
internal sealed class PurchaseResponseCallback : CallbackMsg {
internal enum EPurchaseResult : int {
internal enum EPurchaseResult : sbyte {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
Unknown = -1,
OK = 0,
AlreadyOwned = 9,
@@ -123,39 +118,35 @@ namespace ArchiSteamFarm {
OnCooldown = 53
}
internal readonly EResult Result;
internal readonly EPurchaseResult PurchaseResult;
internal readonly KeyValue ReceiptInfo;
internal readonly Dictionary<uint, string> Items;
internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
JobID = jobID;
if (msg == null) {
return;
if ((jobID == null) || (msg == null)) {
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
}
Result = (EResult) msg.eresult;
JobID = jobID;
PurchaseResult = (EPurchaseResult) msg.purchase_result_details;
if (msg.purchase_receipt_info == null) {
return;
}
ReceiptInfo = new KeyValue();
KeyValue receiptInfo = new KeyValue();
using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) {
if (!ReceiptInfo.TryReadAsBinary(ms)) {
if (!receiptInfo.TryReadAsBinary(ms)) {
return;
}
List<KeyValue> lineItems = ReceiptInfo["lineitems"].Children;
List<KeyValue> lineItems = receiptInfo["lineitems"].Children;
Items = new Dictionary<uint, string>(lineItems.Count);
foreach (KeyValue lineItem in lineItems) {
uint appID = (uint) lineItem["PackageID"].AsUnsignedLong();
string gameName = lineItem["ItemDescription"].Value;
gameName = WebUtility.HtmlDecode(gameName); // Apparently steam expects client to decode sent HTML
Items.Add(appID, gameName);
Items[appID] = gameName;
}
}
}
@@ -170,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) {
if (!Client.IsConnected) {
return;
}
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
var gamePlayed = new CMsgClientGamesPlayed.GamePlayed();
ClientMsgProtobuf<CMsgClientGamesPlayed> request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
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 {
game_id = new GameID(gameID),
game_extra_info = gameName,
game_id = new GameID {
AppType = GameID.GameType.Shortcut,
ModID = uint.MaxValue
}
});
}
Client.Send(request);
}
internal void PlayGames(ICollection<uint> gameIDs) {
if (gameIDs == null || !Client.IsConnected) {
internal void PlayGames(uint gameID) {
if (!Client.IsConnected) {
return;
}
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
foreach (uint gameID in gameIDs) {
if (gameID == 0) {
continue;
}
PlayGames(new HashSet<uint> { gameID });
}
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 {
game_id = new GameID(gameID),
game_id = new GameID(gameID)
});
}
@@ -258,7 +208,7 @@ namespace ArchiSteamFarm {
return null;
}
var request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey) {
ClientMsgProtobuf<CMsgClientRegisterKey> request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey) {
SourceJobID = Client.GetNextJobID()
};
@@ -282,8 +232,11 @@ namespace ArchiSteamFarm {
SteamID steamID = new SteamID(details.AccountID, details.AccountInstance, Client.ConnectedUniverse, EAccountType.Individual);
var logon = new ClientMsgProtobuf<CMsgClientLogon>(EMsg.ClientLogon);
logon.Body.obfustucated_private_ip = details.LoginID.Value;
ClientMsgProtobuf<CMsgClientLogon> logon = new ClientMsgProtobuf<CMsgClientLogon>(EMsg.ClientLogon);
if (details.LoginID != null) {
logon.Body.obfustucated_private_ip = details.LoginID.Value;
}
logon.ProtoHeader.client_sessionid = 0;
logon.ProtoHeader.steamid = steamID.ConvertToUInt64();
logon.Body.account_name = details.Username;
@@ -339,7 +292,7 @@ namespace ArchiSteamFarm {
return;
}
var response = new ClientMsgProtobuf<CMsgClientOfflineMessageNotification>(packetMsg);
ClientMsgProtobuf<CMsgClientOfflineMessageNotification> response = new ClientMsgProtobuf<CMsgClientOfflineMessageNotification>(packetMsg);
Client.PostCallback(new OfflineMessageCallback(packetMsg.TargetJobID, response.Body));
}
@@ -348,7 +301,7 @@ namespace ArchiSteamFarm {
return;
}
var response = new ClientMsgProtobuf<CMsgClientItemAnnouncements>(packetMsg);
ClientMsgProtobuf<CMsgClientItemAnnouncements> response = new ClientMsgProtobuf<CMsgClientItemAnnouncements>(packetMsg);
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
}
@@ -357,7 +310,7 @@ namespace ArchiSteamFarm {
return;
}
var response = new ClientMsgProtobuf<CMsgClientPurchaseResponse>(packetMsg);
ClientMsgProtobuf<CMsgClientPurchaseResponse> response = new ClientMsgProtobuf<CMsgClientPurchaseResponse>(packetMsg);
Client.PostCallback(new PurchaseResponseCallback(packetMsg.TargetJobID, response.Body));
}
@@ -366,7 +319,7 @@ namespace ArchiSteamFarm {
return;
}
var response = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
ClientMsgProtobuf<CMsgClientUserNotifications> response = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
}
}

View File

@@ -99,10 +99,11 @@
<Compile Include="ArchiWebHandler.cs" />
<Compile Include="Bot.cs" />
<Compile Include="BotConfig.cs" />
<Compile Include="ConcurrentEnumerator.cs" />
<Compile Include="ConcurrentHashSet.cs" />
<Compile Include="GlobalDatabase.cs" />
<Compile Include="BotDatabase.cs" />
<Compile Include="CardsFarmer.cs" />
<Compile Include="CMsgs\CMsgClientClanInviteAction.cs" />
<Compile Include="Debugging.cs" />
<Compile Include="GlobalConfig.cs" />
<Compile Include="JSON\GitHub.cs" />

View File

@@ -32,6 +32,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Threading;
using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
internal sealed class ArchiWebHandler {
@@ -63,11 +64,7 @@ namespace ArchiSteamFarm {
}
uint appID;
if (!uint.TryParse(hashName.Substring(0, index), out appID)) {
return 0;
}
return appID;
return !uint.TryParse(hashName.Substring(0, index), out appID) ? 0 : appID;
}
private static Steam.Item.EType GetItemType(string name) {
@@ -87,21 +84,23 @@ namespace ArchiSteamFarm {
default:
if (name.EndsWith("Emoticon", StringComparison.Ordinal)) {
return Steam.Item.EType.Emoticon;
} else if (name.EndsWith("Foil Trading Card", StringComparison.Ordinal)) {
return Steam.Item.EType.FoilTradingCard;
} else if (name.EndsWith("Profile Background", StringComparison.Ordinal)) {
return Steam.Item.EType.ProfileBackground;
} else if (name.EndsWith("Trading Card", StringComparison.Ordinal)) {
return Steam.Item.EType.TradingCard;
} else {
return Steam.Item.EType.Unknown;
}
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) {
if (bot == null) {
throw new ArgumentNullException("bot");
throw new ArgumentNullException(nameof(bot));
}
Bot = bot;
@@ -110,11 +109,14 @@ namespace ArchiSteamFarm {
}
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;
}
ulong steamID = steamClient.SteamID;
if (steamID == 0) {
return false;
}
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
@@ -198,16 +200,16 @@ namespace ArchiSteamFarm {
};
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).ConfigureAwait(false);
}
if (!result) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
if (result) {
return true;
}
return true;
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
internal async Task<bool> JoinGroup(ulong groupID) {
@@ -232,16 +234,16 @@ namespace ArchiSteamFarm {
};
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).ConfigureAwait(false);
}
if (!result) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
if (result) {
return true;
}
return true;
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
@@ -252,7 +254,7 @@ namespace ArchiSteamFarm {
string request = SteamCommunityURL + "/my/games/?xml=1";
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);
}
@@ -262,7 +264,7 @@ namespace ArchiSteamFarm {
}
XmlNodeList xmlNodeList = response.SelectNodes("gamesList/games/game");
if (xmlNodeList == null || xmlNodeList.Count == 0) {
if ((xmlNodeList == null) || (xmlNodeList.Count == 0)) {
return null;
}
@@ -290,7 +292,7 @@ namespace ArchiSteamFarm {
}
internal Dictionary<uint, string> GetOwnedGames(ulong steamID) {
if (steamID == 0 || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
if ((steamID == 0) || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
return null;
}
@@ -298,7 +300,7 @@ namespace ArchiSteamFarm {
using (dynamic iPlayerService = WebAPI.GetInterface("IPlayerService", Bot.BotConfig.SteamApiKey)) {
iPlayerService.Timeout = Timeout;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
try {
response = iPlayerService.GetOwnedGames(
steamid: steamID,
@@ -338,7 +340,7 @@ namespace ArchiSteamFarm {
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
iEconService.Timeout = Timeout;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
try {
response = iEconService.GetTradeOffers(
get_received_offers: 1,
@@ -389,7 +391,6 @@ namespace ArchiSteamFarm {
HashSet<Steam.TradeOffer> result = new HashSet<Steam.TradeOffer>();
foreach (KeyValue trade in response["trade_offers_received"].Children) {
// TODO: Correct some of these when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released
Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
TradeOfferID = trade["tradeofferid"].AsUnsignedLong(),
OtherSteamID3 = (uint) trade["accountid_other"].AsUnsignedLong(),
@@ -468,16 +469,16 @@ namespace ArchiSteamFarm {
};
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);
}
if (!result) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
if (result) {
return true;
}
return true;
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
internal async Task<HashSet<Steam.Item>> GetMyTradableInventory() {
@@ -492,7 +493,7 @@ namespace ArchiSteamFarm {
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1&start=" + nextPage;
JObject jObject = null;
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
for (byte i = 0; (i < WebBrowser.MaxRetries) && (jObject == null); i++) {
jObject = await WebBrowser.UrlGetToJObject(request).ConfigureAwait(false);
}
@@ -514,7 +515,7 @@ namespace ArchiSteamFarm {
}
ulong classID;
if (!ulong.TryParse(classIDString, out classID) || classID == 0) {
if (!ulong.TryParse(classIDString, out classID) || (classID == 0)) {
continue;
}
@@ -550,7 +551,7 @@ namespace ArchiSteamFarm {
}
IEnumerable<JToken> items = jObject.SelectTokens("$.rgInventory.*");
if (descriptions == null) {
if (items == null) {
return null;
}
@@ -594,7 +595,7 @@ namespace ArchiSteamFarm {
}
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;
}
@@ -608,13 +609,12 @@ namespace ArchiSteamFarm {
return false;
}
HashSet<Steam.TradeOfferRequest> trades = new HashSet<Steam.TradeOfferRequest>();
Steam.TradeOfferRequest singleTrade = null;
Steam.TradeOfferRequest singleTrade = new Steam.TradeOfferRequest();
HashSet<Steam.TradeOfferRequest> trades = new HashSet<Steam.TradeOfferRequest> { singleTrade };
byte itemID = 0;
foreach (Steam.Item item in inventory) {
if (itemID % Trading.MaxItemsPerTrade == 0) {
if (itemID >= Trading.MaxItemsPerTrade) {
if (trades.Count >= Trading.MaxTradesPerAccount) {
break;
}
@@ -624,7 +624,7 @@ namespace ArchiSteamFarm {
itemID = 0;
}
singleTrade.ItemsToGive.Assets.Add(new Steam.Item() {
singleTrade.ItemsToGive.Assets.Add(new Steam.Item {
AppID = Steam.Item.SteamAppID,
ContextID = Steam.Item.SteamContextID,
Amount = item.Amount,
@@ -647,14 +647,16 @@ namespace ArchiSteamFarm {
};
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);
}
if (!result) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
if (result) {
continue;
}
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
return true;
@@ -672,16 +674,16 @@ namespace ArchiSteamFarm {
string request = SteamCommunityURL + "/my/badges?p=" + page;
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(request).ConfigureAwait(false);
}
if (htmlDocument == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
if (htmlDocument != null) {
return htmlDocument;
}
return htmlDocument;
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
}
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
@@ -696,16 +698,16 @@ namespace ArchiSteamFarm {
string request = SteamCommunityURL + "/my/gamecards/" + appID;
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(request).ConfigureAwait(false);
}
if (htmlDocument == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
if (htmlDocument != null) {
return htmlDocument;
}
return htmlDocument;
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
}
internal async Task<bool> MarkInventory() {
@@ -716,32 +718,32 @@ namespace ArchiSteamFarm {
string request = SteamCommunityURL + "/my/inventory";
bool result = false;
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
for (byte i = 0; (i < WebBrowser.MaxRetries) && !result; i++) {
result = await WebBrowser.UrlHead(request).ConfigureAwait(false);
}
if (!result) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
if (result) {
return true;
}
return true;
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
private async Task<bool?> IsLoggedIn() {
string request = SteamCommunityURL + "/my/profile";
Uri uri = null;
for (byte i = 0; i < WebBrowser.MaxRetries && uri == null; i++) {
for (byte i = 0; (i < WebBrowser.MaxRetries) && (uri == null); i++) {
uri = await WebBrowser.UrlHeadToUri(request).ConfigureAwait(false);
}
if (uri == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
if (uri != null) {
return !uri.AbsolutePath.StartsWith("/login", StringComparison.Ordinal);
}
return !uri.AbsolutePath.StartsWith("/login", StringComparison.Ordinal);
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
}
private async Task<bool> RefreshSessionIfNeeded() {
@@ -784,7 +786,7 @@ namespace ArchiSteamFarm {
};
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, SteamCommunityURL).ConfigureAwait(false);
}

File diff suppressed because it is too large Load Diff

View File

@@ -28,6 +28,8 @@ using System.Collections.Generic;
using System.IO;
namespace ArchiSteamFarm {
// ReSharper disable once ClassCannotBeInstantiated
// ReSharper disable once ClassNeverInstantiated.Global
internal sealed class BotConfig {
[JsonProperty(Required = Required.DisallowNull)]
internal bool Enabled { get; private set; } = false;
@@ -36,10 +38,10 @@ namespace ArchiSteamFarm {
internal bool StartOnLaunch { get; private set; } = true;
[JsonProperty]
internal string SteamLogin { get; set; } = null;
internal string SteamLogin { get; set; }
[JsonProperty]
internal string SteamPassword { get; set; } = null;
internal string SteamPassword { get; set; }
[JsonProperty]
internal string SteamParentalPIN { get; set; } = "0";
@@ -99,7 +101,7 @@ namespace ArchiSteamFarm {
internal string CustomGamePlayedWhileIdle { get; private set; } = null;
[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) {

View File

@@ -25,6 +25,7 @@
using Newtonsoft.Json;
using SteamAuth;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace ArchiSteamFarm {
@@ -93,7 +94,7 @@ namespace ArchiSteamFarm {
// This constructor is used when creating new database
private BotDatabase(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException("filePath");
throw new ArgumentNullException(nameof(filePath));
}
FilePath = filePath;
@@ -101,6 +102,7 @@ namespace ArchiSteamFarm {
}
// This constructor is used only by deserializer
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private BotDatabase() { }
internal void Save() {

View File

@@ -35,7 +35,7 @@ using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal sealed class CardsFarmer {
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 ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
private readonly SemaphoreSlim FarmingSemaphore = new SemaphoreSlim(1);
@@ -48,12 +48,12 @@ namespace ArchiSteamFarm {
internal CardsFarmer(Bot bot) {
if (bot == null) {
throw new ArgumentNullException("bot");
throw new ArgumentNullException(nameof(bot));
}
Bot = bot;
if (Timer == null && Program.GlobalConfig.IdleFarmingPeriod > 0) {
if ((Timer == null) && (Program.GlobalConfig.IdleFarmingPeriod > 0)) {
Timer = new Timer(
async e => await CheckGamesForFarming().ConfigureAwait(false),
null,
@@ -94,7 +94,7 @@ namespace ArchiSteamFarm {
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
Logging.LogGenericInfo("We don't have anything to farm on this account!", Bot.BotName);
await Bot.OnFarmingFinished().ConfigureAwait(false);
await Bot.OnFarmingFinished(false).ConfigureAwait(false);
return;
}
@@ -132,20 +132,21 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Chosen farming algorithm: Simple", Bot.BotName);
while (GamesToFarm.Count > 0) {
uint appID = GamesToFarm.Keys.FirstOrDefault();
if (!await FarmSolo(appID).ConfigureAwait(false)) {
NowFarming = false;
return;
if (await FarmSolo(appID).ConfigureAwait(false)) {
continue;
}
NowFarming = false;
return;
}
}
} while (await IsAnythingToFarm().ConfigureAwait(false));
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
CurrentGamesFarming.ClearAndTrim();
NowFarming = false;
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
await Bot.OnFarmingFinished().ConfigureAwait(false);
await Bot.OnFarmingFinished(true).ConfigureAwait(false);
}
internal async Task StopFarming() {
@@ -164,7 +165,7 @@ namespace ArchiSteamFarm {
FarmResetEvent.Set();
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);
}
@@ -188,10 +189,8 @@ namespace ArchiSteamFarm {
}
HashSet<uint> result = new HashSet<uint>();
foreach (KeyValuePair<uint, float> keyValue in gamesToFarm) {
if (keyValue.Value >= 2) {
result.Add(keyValue.Key);
}
foreach (KeyValuePair<uint, float> keyValue in gamesToFarm.Where(keyValue => keyValue.Value >= 2)) {
result.Add(keyValue.Key);
}
return result;
@@ -210,7 +209,7 @@ namespace ArchiSteamFarm {
byte maxPages = 1;
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];
string lastPage = htmlNode.InnerText;
if (!string.IsNullOrEmpty(lastPage)) {
@@ -224,16 +223,19 @@ namespace ArchiSteamFarm {
CheckPage(htmlDocument);
if (maxPages > 1) {
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);
if (maxPages <= 1) {
return GamesToFarm.Count > 0;
}
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;
}
@@ -364,21 +366,15 @@ namespace ArchiSteamFarm {
}
if (maxHour >= 2) {
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
CurrentGamesFarming.ClearAndTrim();
return true;
}
Logging.LogGenericInfo("Now farming: " + string.Join(", ", CurrentGamesFarming), Bot.BotName);
if (FarmHours(maxHour, CurrentGamesFarming)) {
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
return true;
} else {
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
return false;
}
bool result = FarmHours(maxHour, CurrentGamesFarming);
CurrentGamesFarming.ClearAndTrim();
return result;
}
private async Task<bool> FarmSolo(uint appID) {
@@ -389,20 +385,22 @@ namespace ArchiSteamFarm {
CurrentGamesFarming.Add(appID);
Logging.LogGenericInfo("Now farming: " + appID, Bot.BotName);
if (await Farm(appID).ConfigureAwait(false)) {
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
float hours;
if (GamesToFarm.TryRemove(appID, out hours)) {
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();
bool result = await Farm(appID).ConfigureAwait(false);
CurrentGamesFarming.ClearAndTrim();
if (!result) {
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) {
@@ -415,7 +413,7 @@ namespace ArchiSteamFarm {
bool success = true;
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.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
success = false;
break;
@@ -434,8 +432,8 @@ namespace ArchiSteamFarm {
return success;
}
private bool FarmHours(float maxHour, HashSet<uint> appIDs) {
if (maxHour < 0 || appIDs == null || appIDs.Count == 0) {
private bool FarmHours(float maxHour, ConcurrentHashSet<uint> appIDs) {
if ((maxHour < 0) || (appIDs == null) || (appIDs.Count == 0)) {
return false;
}

View File

@@ -22,45 +22,37 @@
*/
using SteamKit2;
using SteamKit2.Internal;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace ArchiSteamFarm {
internal sealed class CMsgClientClanInviteAction : ISteamSerializableMessage {
internal ulong GroupID { get; set; } = 0;
internal bool AcceptInvite { get; set; } = true;
internal sealed class ConcurrentEnumerator<T> : IEnumerator<T> {
public T Current => Enumerator.Current;
EMsg ISteamSerializableMessage.GetEMsg() {
return EMsg.ClientAcknowledgeClanInvite;
object IEnumerator.Current => Current;
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) {
if (stream == null) {
return;
}
public bool MoveNext() => Enumerator.MoveNext();
public void Reset() => Enumerator.Reset();
try {
BinaryWriter binaryWriter = new BinaryWriter(stream);
binaryWriter.Write(GroupID);
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);
public void Dispose() {
if (Lock != null) {
Lock.ExitReadLock();
}
}
}

View 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();
}
}

View File

@@ -24,19 +24,20 @@
using SteamKit2;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace ArchiSteamFarm {
internal static class Debugging {
#if DEBUG
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal static readonly bool IsDebugBuild = true;
#else
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal static readonly bool IsDebugBuild = false;
#endif
internal static bool IsReleaseBuild => !IsDebugBuild;
internal static bool NetHookAlreadyInitialized { get; set; } = false;
internal static bool NetHookAlreadyInitialized { get; set; }
internal sealed class DebugListener : IDebugListener {
private readonly string FilePath;

View File

@@ -25,11 +25,14 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Sockets;
namespace ArchiSteamFarm {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated"), SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
internal sealed class GlobalConfig {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal enum EUpdateChannel : byte {
Unknown,
Stable,
@@ -160,11 +163,13 @@ namespace ArchiSteamFarm {
globalConfig.HttpTimeout = DefaultHttpTimeout;
}
if (globalConfig.WCFPort == 0) {
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
globalConfig.WCFPort = DefaultWCFPort;
if (globalConfig.WCFPort != 0) {
return globalConfig;
}
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
globalConfig.WCFPort = DefaultWCFPort;
return globalConfig;
}

View File

@@ -24,6 +24,7 @@
using Newtonsoft.Json;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace ArchiSteamFarm {
@@ -75,7 +76,7 @@ namespace ArchiSteamFarm {
// This constructor is used when creating new database
private GlobalDatabase(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException("filePath");
throw new ArgumentNullException(nameof(filePath));
}
FilePath = filePath;
@@ -83,6 +84,7 @@ namespace ArchiSteamFarm {
}
// This constructor is used only by deserializer
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private GlobalDatabase() { }
private void Save() {

View File

@@ -22,11 +22,13 @@
*/
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm {
namespace ArchiSteamFarm.JSON {
internal static class GitHub {
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global"), SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
internal sealed class ReleaseResponse {
internal sealed class Asset {
[JsonProperty(PropertyName = "name", Required = Required.Always)]

View File

@@ -22,11 +22,13 @@
*/
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Newtonsoft.Json;
using SteamKit2;
using System.Collections.Generic;
namespace ArchiSteamFarm {
namespace ArchiSteamFarm.JSON {
internal static class Steam {
internal sealed class Item { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
internal const ushort SteamAppID = 753;
@@ -48,11 +50,12 @@ namespace ArchiSteamFarm {
internal uint AppID { get; set; }
[JsonProperty(PropertyName = "appid", Required = Required.DisallowNull)]
internal string AppIDString {
[JsonProperty(PropertyName = "appid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
private string AppIDString {
get {
return AppID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
@@ -69,11 +72,12 @@ namespace ArchiSteamFarm {
internal ulong ContextID { get; set; }
[JsonProperty(PropertyName = "contextid", Required = Required.DisallowNull)]
internal string ContextIDString {
[JsonProperty(PropertyName = "contextid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
private string ContextIDString {
get {
return ContextID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
@@ -91,10 +95,11 @@ namespace ArchiSteamFarm {
internal ulong AssetID { get; set; }
[JsonProperty(PropertyName = "assetid", Required = Required.DisallowNull)]
internal string AssetIDString {
private string AssetIDString {
get {
return AssetID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
@@ -109,19 +114,20 @@ namespace ArchiSteamFarm {
}
}
[JsonProperty(PropertyName = "id", Required = Required.DisallowNull)]
internal string ID {
[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)]
internal string ClassIDString {
[JsonProperty(PropertyName = "classid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
private string ClassIDString {
get {
return ClassID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
@@ -138,11 +144,12 @@ namespace ArchiSteamFarm {
internal ulong InstanceID { get; set; }
[JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull)]
internal string InstanceIDString {
[JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
private string InstanceIDString {
get {
return InstanceID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
@@ -159,11 +166,12 @@ namespace ArchiSteamFarm {
internal uint Amount { get; set; }
[JsonProperty(PropertyName = "amount", Required = Required.Always)]
internal string AmountString {
[JsonProperty(PropertyName = "amount", Required = Required.Always), SuppressMessage("ReSharper", "UnusedMember.Local")]
private string AmountString {
get {
return Amount.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
@@ -183,6 +191,7 @@ namespace ArchiSteamFarm {
}
internal sealed class TradeOffer { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal enum ETradeOfferState : byte {
Unknown,
Invalid,
@@ -200,11 +209,12 @@ namespace ArchiSteamFarm {
internal ulong TradeOfferID { get; set; }
[JsonProperty(PropertyName = "tradeofferid", Required = Required.Always)]
internal string TradeOfferIDString {
[JsonProperty(PropertyName = "tradeofferid", Required = Required.Always), SuppressMessage("ReSharper", "UnusedMember.Local")]
private string TradeOfferIDString {
get {
return TradeOfferID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
@@ -220,7 +230,7 @@ namespace ArchiSteamFarm {
}
[JsonProperty(PropertyName = "accountid_other", Required = Required.Always)]
internal uint OtherSteamID3 { get; set; }
internal uint OtherSteamID3 { private get; set; }
[JsonProperty(PropertyName = "trade_offer_state", Required = Required.Always)]
internal ETradeOfferState State { get; set; }
@@ -232,88 +242,57 @@ namespace ArchiSteamFarm {
internal HashSet<Item> ItemsToReceive { get; } = new HashSet<Item>();
// Extra
internal ulong OtherSteamID64 {
get {
if (OtherSteamID3 == 0) {
return 0;
}
internal ulong OtherSteamID64 => OtherSteamID3 == 0 ? 0 : new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
return new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
}
set {
if (value == 0) {
return;
}
internal bool IsSteamCardsOnlyTradeForUs() => ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamContextID) && ((item.Type == Item.EType.FoilTradingCard) || (item.Type == Item.EType.TradingCard)));
OtherSteamID3 = new SteamID(value).AccountID;
}
}
internal bool IsSteamCardsOnlyTrade() {
internal bool IsPotentiallyDupesTradeForUs() {
Dictionary<uint, Dictionary<Item.EType, uint>> itemsToGivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
foreach (Item item in ItemsToGive) {
if (item.AppID != Item.SteamAppID || item.ContextID != Item.SteamContextID || (item.Type != Item.EType.FoilTradingCard && item.Type != Item.EType.TradingCard)) {
return false;
}
}
foreach (Item item in ItemsToReceive) {
if (item.AppID != Item.SteamAppID || item.ContextID != Item.SteamContextID || (item.Type != Item.EType.FoilTradingCard && item.Type != Item.EType.TradingCard)) {
return false;
}
}
return true;
}
internal bool IsPotentiallyDupesTrade() {
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>();
ItemsPerType[item.Type] = item.Amount;
ItemsToGivePerGame[item.RealAppID] = ItemsPerType;
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;
if (itemsPerType.TryGetValue(item.Type, out amount)) {
itemsPerType[item.Type] = amount + item.Amount;
} else {
ItemsPerType[item.Type] = item.Amount;
itemsPerType[item.Type] = item.Amount;
}
}
}
Dictionary<uint, Dictionary<Item.EType, uint>> ItemsToReceivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
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>();
ItemsPerType[item.Type] = item.Amount;
ItemsToReceivePerGame[item.RealAppID] = ItemsPerType;
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;
if (itemsPerType.TryGetValue(item.Type, out amount)) {
itemsPerType[item.Type] = amount + item.Amount;
} else {
ItemsPerType[item.Type] = item.Amount;
itemsPerType[item.Type] = item.Amount;
}
}
}
// Ensure that amount per type and per game matches
foreach (KeyValuePair<uint, Dictionary<Item.EType, uint>> ItemsPerGame in ItemsToGivePerGame) {
// 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)) {
if (!itemsToReceivePerGame.TryGetValue(itemsPerGame.Key, out otherItemsPerType)) {
return false;
}
foreach (KeyValuePair<Item.EType, uint> ItemsPerType in ItemsPerGame.Value) {
foreach (KeyValuePair<Item.EType, uint> itemsPerType in itemsPerGame.Value) {
uint otherAmount;
if (!otherItemsPerType.TryGetValue(ItemsPerType.Key, out otherAmount)) {
if (!otherItemsPerType.TryGetValue(itemsPerType.Key, out otherAmount)) {
return false;
}
if (ItemsPerType.Value != otherAmount) {
if (itemsPerType.Value > otherAmount) {
return false;
}
}
@@ -323,6 +302,7 @@ namespace ArchiSteamFarm {
}
}
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal sealed class TradeOfferRequest {
internal sealed class ItemList {
[JsonProperty(PropertyName = "assets", Required = Required.Always)]

View File

@@ -24,6 +24,7 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
@@ -36,18 +37,25 @@ namespace ArchiSteamFarm {
internal static void Init() {
LogToFile = Program.GlobalConfig.LogToFile;
if (LogToFile) {
lock (FileLock) {
try {
File.Delete(Program.LogFile);
} catch (Exception e) {
LogGenericException(e);
}
if (!LogToFile) {
return;
}
lock (FileLock) {
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)) {
return;
}
@@ -55,7 +63,7 @@ namespace ArchiSteamFarm {
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)) {
return;
}
@@ -63,20 +71,25 @@ namespace ArchiSteamFarm {
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (exception == null) {
return;
}
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
while (true) {
if (exception == null) {
return;
}
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
Log("[!] StackTrace:" + Environment.NewLine + exception.StackTrace);
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
Log("[!] StackTrace:" + Environment.NewLine + exception.StackTrace);
if (exception.InnerException != null) {
LogGenericException(exception.InnerException, botName, previousMethodName);
if (exception.InnerException != null) {
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)) {
return;
}
@@ -84,7 +97,7 @@ namespace ArchiSteamFarm {
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)) {
return;
}
@@ -92,7 +105,8 @@ namespace ArchiSteamFarm {
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)) {
return;
}
@@ -100,8 +114,8 @@ namespace ArchiSteamFarm {
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
}
[Conditional("DEBUG")]
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
[Conditional("DEBUG"), SuppressMessage("ReSharper", "UnusedMember.Global")]
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
if (string.IsNullOrEmpty(message)) {
return;
}
@@ -120,17 +134,26 @@ namespace ArchiSteamFarm {
if (!Program.ConsoleIsBusy) {
try {
Console.Write(loggedMessage);
} catch { }
}
catch {
// Ignored
}
}
if (LogToFile) {
lock (FileLock) {
try {
File.AppendAllText(Program.LogFile, loggedMessage);
} catch (Exception e) {
LogToFile = false;
LogGenericException(e);
}
if (!LogToFile) {
return;
}
lock (FileLock) {
if (!LogToFile) {
return;
}
try {
File.AppendAllText(Program.LogFile, loggedMessage);
} catch (Exception e) {
LogToFile = false;
LogGenericException(e);
}
}
}

View File

@@ -26,11 +26,13 @@ using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
internal static class Program {
@@ -49,21 +51,22 @@ namespace ArchiSteamFarm {
}
private enum EMode : byte {
[SuppressMessage("ReSharper", "UnusedMember.Local")]
Unknown,
Normal, // Standard most common usage
Client, // WCF client only
Server // Normal + WCF server
}
internal const string ASF = "ASF";
internal const string ConfigDirectory = "config";
internal const string DebugDirectory = "debug";
internal const string LogFile = "log.txt";
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 GlobalConfigFile = ASF + ".json";
private const string GlobalDatabaseFile = ASF + ".db";
internal static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
@@ -76,7 +79,7 @@ namespace ArchiSteamFarm {
internal static GlobalConfig GlobalConfig { 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 EMode Mode = EMode.Normal;
@@ -109,7 +112,7 @@ namespace ArchiSteamFarm {
string response = null;
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);
}
@@ -135,7 +138,7 @@ namespace ArchiSteamFarm {
return;
}
if (releases == null || releases.Count == 0) {
if ((releases == null) || (releases.Count == 0)) {
Logging.LogGenericWarning("Could not check latest version!");
return;
}
@@ -153,15 +156,19 @@ namespace ArchiSteamFarm {
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 (AutoUpdatesTimer == null && GlobalConfig.AutoUpdates) {
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
);
if ((AutoUpdatesTimer != null) || !GlobalConfig.AutoUpdates) {
return;
}
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;
}
@@ -183,15 +190,7 @@ namespace ArchiSteamFarm {
return;
}
GitHub.ReleaseResponse.Asset binaryAsset = null;
foreach (var asset in releaseResponse.Assets) {
if (string.IsNullOrEmpty(asset.Name) || !asset.Name.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase)) {
continue;
}
binaryAsset = asset;
break;
}
GitHub.ReleaseResponse.Asset binaryAsset = releaseResponse.Assets.FirstOrDefault(asset => !string.IsNullOrEmpty(asset.Name) && asset.Name.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase));
if (binaryAsset == null) {
Logging.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!");
@@ -204,7 +203,7 @@ namespace ArchiSteamFarm {
}
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...");
result = await WebBrowser.UrlGetToBytes(binaryAsset.DownloadURL).ConfigureAwait(false);
}
@@ -232,7 +231,9 @@ namespace ArchiSteamFarm {
try {
// Cleanup
File.Delete(newExeFile);
} catch { }
} catch {
// Ignored
}
return;
}
@@ -245,7 +246,9 @@ namespace ArchiSteamFarm {
// Cleanup
File.Move(oldExeFile, ExecutableFile);
File.Delete(newExeFile);
} catch { }
} catch {
// Ignored
}
return;
}
@@ -326,6 +329,7 @@ namespace ArchiSteamFarm {
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter not documented yet value of \"" + userInputType + "\": ");
break;
}
result = Console.ReadLine();
if (!Console.IsOutputRedirected) {
Console.Clear(); // For security purposes
@@ -337,10 +341,8 @@ namespace ArchiSteamFarm {
}
internal static void OnBotShutdown() {
foreach (Bot bot in Bot.Bots.Values) {
if (bot.KeepRunning) {
return;
}
if (Bot.Bots.Values.Any(bot => bot.KeepRunning)) {
return;
}
if (WCF.IsServerRunning()) {
@@ -374,7 +376,11 @@ namespace ArchiSteamFarm {
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) {
switch (arg) {
case "--client":
@@ -385,7 +391,7 @@ namespace ArchiSteamFarm {
WCF.StartServer();
break;
default:
if (arg.StartsWith("--")) {
if (arg.StartsWith("--", StringComparison.Ordinal)) {
Logging.LogGenericWarning("Unrecognized parameter: " + arg);
continue;
}
@@ -410,7 +416,7 @@ namespace ArchiSteamFarm {
}
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
if (sender == null || args == null) {
if ((sender == null) || (args == null)) {
return;
}
@@ -418,14 +424,14 @@ namespace ArchiSteamFarm {
}
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
if (sender == null || args == null) {
if ((sender == null) || (args == null)) {
return;
}
Logging.LogGenericException(args.Exception);
}
private static void Init(string[] args) {
private static void Init(IEnumerable<string> args) {
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
@@ -437,7 +443,7 @@ namespace ArchiSteamFarm {
if (Debugging.IsDebugBuild) {
// 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("..");
if (Directory.Exists(ConfigDirectory)) {
break;
@@ -458,7 +464,7 @@ namespace ArchiSteamFarm {
}
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;
}
@@ -486,8 +492,7 @@ namespace ArchiSteamFarm {
bool isRunning = false;
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.json")) {
string botName = Path.GetFileNameWithoutExtension(configFile);
foreach (string botName in Directory.EnumerateFiles(ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension)) {
switch (botName) {
case ASF:
case "example":
@@ -496,7 +501,7 @@ namespace ArchiSteamFarm {
}
Bot bot = new Bot(botName);
if (bot.BotConfig != null && bot.BotConfig.Enabled) {
if ((bot.BotConfig != null) && bot.BotConfig.Enabled) {
if (bot.BotConfig.StartOnLaunch) {
isRunning = true;
}

View File

@@ -1,8 +1,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
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
// associated with an assembly.
[assembly: AssemblyTitle("ArchiSteamFarm")]
@@ -14,8 +13,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 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
// 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
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
@@ -25,12 +24,12 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Minor Version
// Build Number
// 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:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.4.1")]
[assembly: AssemblyFileVersion("2.0.4.1")]
[assembly: AssemblyVersion("2.0.4.6")]
[assembly: AssemblyFileVersion("2.0.4.6")]

View File

@@ -25,8 +25,10 @@
using SteamAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
internal sealed class Trading {
@@ -50,7 +52,7 @@ namespace ArchiSteamFarm {
internal Trading(Bot bot) {
if (bot == null) {
throw new ArgumentNullException("bot");
throw new ArgumentNullException(nameof(bot));
}
Bot = bot;
@@ -77,7 +79,7 @@ namespace ArchiSteamFarm {
private async Task ParseActiveTrades() {
HashSet<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
if (tradeOffers == null || tradeOffers.Count == 0) {
if ((tradeOffers == null) || (tradeOffers.Count == 0)) {
return;
}
@@ -86,7 +88,7 @@ namespace ArchiSteamFarm {
}
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
if (tradeOffer == null || tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active) {
if ((tradeOffer == null) || (tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active)) {
return;
}
@@ -110,7 +112,7 @@ namespace ArchiSteamFarm {
}
// 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;
}
@@ -119,20 +121,20 @@ namespace ArchiSteamFarm {
return false;
}
// Rule 1 - We always trade the same amount of items
if (tradeOffer.ItemsToGive.Count != tradeOffer.ItemsToReceive.Count) {
// Decline trade if we're giving more count-wise
if (tradeOffer.ItemsToGive.Count > tradeOffer.ItemsToReceive.Count) {
return false;
}
// Rule 2 - We always trade steam cards and only for the same set
if (!tradeOffer.IsSteamCardsOnlyTrade() || !tradeOffer.IsPotentiallyDupesTrade()) {
// 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) {
if ((inventory == null) || (inventory.Count == 0)) {
return true; // OK, assume that this trade is valid, we can't check our EQ
}
@@ -165,34 +167,40 @@ namespace ArchiSteamFarm {
}
// Calculate our value of items to give
uint itemsToGiveDupesValue = 0;
foreach (Steam.Item item in tradeOffer.ItemsToGive) {
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(item.ClassID, item.InstanceID);
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;
}
itemsToGiveDupesValue += amount;
amountsToGive.Add(amount);
}
// Sort it ascending
amountsToGive.Sort();
// Calculate our value of items to receive
uint itemsToReceiveDupesValue = 0;
foreach (Steam.Item item in tradeOffer.ItemsToReceive) {
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(item.ClassID, item.InstanceID);
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;
}
itemsToReceiveDupesValue += amount;
amountsToReceive.Add(amount);
}
// Trade is worth for us if we're in total trading more of our dupes for less of our dupes (or at least same amount)
// Which means that itemsToGiveDupesValue should be greater than itemsToReceiveDupesValue
return itemsToGiveDupesValue > itemsToReceiveDupesValue;
// 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;
}
}
}

View File

@@ -24,49 +24,27 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal static class Utilities {
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
internal static void Forget(this Task task) { }
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
if (action == null) {
return Task.FromResult(true);
}
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) {
if (string.IsNullOrEmpty(URL) || string.IsNullOrEmpty(name)) {
internal static string GetCookieValue(this CookieContainer cookieContainer, string url, string name) {
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(name)) {
return null;
}
CookieCollection cookies = cookieContainer.GetCookies(new Uri(URL));
if (cookies == null || cookies.Count == 0) {
return null;
}
foreach (Cookie cookie in cookies) {
if (!cookie.Name.Equals(name, StringComparison.Ordinal)) {
continue;
}
return cookie.Value;
}
return null;
CookieCollection cookies = cookieContainer.GetCookies(new Uri(url));
return cookies.Count == 0 ? null : (from Cookie cookie in cookies where cookie.Name.Equals(name, StringComparison.Ordinal) select cookie.Value).FirstOrDefault();
}
internal static Task SleepAsync(int miliseconds) {
if (miliseconds < 0) {
return Task.FromResult(true);
}
return Task.Delay(miliseconds);
}
internal static Task SleepAsync(int miliseconds) => miliseconds < 0 ? Task.FromResult(true) : Task.Delay(miliseconds);
}
}

View File

@@ -52,9 +52,7 @@ namespace ArchiSteamFarm {
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
}
internal bool IsServerRunning() {
return ServiceHost != null;
}
internal bool IsServerRunning() => ServiceHost != null;
internal void StartServer() {
if (ServiceHost != null) {
@@ -101,35 +99,18 @@ namespace ArchiSteamFarm {
return null;
}
string[] args = input.Split(' ');
string botName;
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();
Bot bot = Bot.Bots.Values.FirstOrDefault();
if (bot == null) {
return "ERROR: No bots are enabled!";
}
if (string.IsNullOrEmpty(botName)) {
return "ERROR: Invalid botName: " + botName;
}
Bot bot;
if (!Bot.Bots.TryGetValue(botName, out bot)) {
return "ERROR: Couldn't find any bot named: " + botName;
}
Logging.LogGenericInfo("Received command: " + input);
string output;
if (Program.GlobalConfig.SteamOwnerID == 0) {
output = "Refusing to handle request because SteamOwnerID is not set!";
} else {
string command = '!' + input;
output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
return "Refusing to handle request because SteamOwnerID is not set!";
}
string command = "!" + input;
string output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
Logging.LogGenericInfo("Answered to command: " + input + " with: " + output);
return output;
}

View File

@@ -27,7 +27,6 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
@@ -65,7 +64,7 @@ namespace ArchiSteamFarm {
internal WebBrowser(string identifier) {
if (string.IsNullOrEmpty(identifier)) {
throw new ArgumentNullException("identifier");
throw new ArgumentNullException(nameof(identifier));
}
Identifier = identifier;
@@ -99,11 +98,7 @@ namespace ArchiSteamFarm {
}
using (HttpResponseMessage response = await UrlHeadToResponse(request, referer).ConfigureAwait(false)) {
if (response == null) {
return null;
}
return response.RequestMessage.RequestUri;
return response == null ? null : response.RequestMessage.RequestUri;
}
}
@@ -229,7 +224,7 @@ namespace ArchiSteamFarm {
}
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;
}
@@ -239,7 +234,7 @@ namespace ArchiSteamFarm {
HttpResponseMessage responseMessage;
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
if (data != null && data.Count > 0) {
if ((data != null) && (data.Count > 0)) {
try {
requestMessage.Content = new FormUrlEncodedContent(data);
} catch (UriFormatException e) {
@@ -263,17 +258,18 @@ namespace ArchiSteamFarm {
return null;
}
if (!responseMessage.IsSuccessStatusCode) {
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;
if (responseMessage.IsSuccessStatusCode) {
return responseMessage;
}
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;
}
}
}

View File

@@ -39,13 +39,13 @@ namespace ConfigGenerator {
protected ASFConfig(string filePath) : this() {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException("filePath");
throw new ArgumentNullException(nameof(filePath));
}
FilePath = filePath;
}
internal virtual void Save() {
internal void Save() {
lock (FilePath) {
try {
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);
lock (FilePath) {
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
@@ -66,10 +66,11 @@ namespace ConfigGenerator {
}
}
}
ASFConfigs.Remove(this);
}
internal virtual void Rename(string botName) {
internal void Rename(string botName) {
if (string.IsNullOrEmpty(botName)) {
return;
}
@@ -83,6 +84,7 @@ namespace ConfigGenerator {
Logging.LogGenericException(e);
}
}
FilePath = Path.Combine(Program.ConfigDirectory, botName + ".json");
}
}

View File

@@ -26,9 +26,11 @@ using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace ConfigGenerator {
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")]
internal sealed class BotConfig : ASFConfig {
[JsonProperty(Required = Required.DisallowNull)]
public bool Enabled { get; set; } = false;
@@ -39,8 +41,7 @@ namespace ConfigGenerator {
[JsonProperty]
public string SteamLogin { get; set; } = null;
[JsonProperty]
[PasswordPropertyText(true)]
[JsonProperty, PasswordPropertyText(true)]
public string SteamPassword { get; set; } = null;
[JsonProperty]
@@ -129,12 +130,12 @@ namespace ConfigGenerator {
return botConfig;
}
// This constructor is used only by deserializer
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private BotConfig() { }
private BotConfig(string filePath) : base(filePath) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException("filePath");
throw new ArgumentNullException(nameof(filePath));
}
GamesPlayedWhileIdle.Add(0);

View File

@@ -83,6 +83,7 @@
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">

View File

@@ -42,8 +42,6 @@ namespace ConfigGenerator {
Controls.Add(enhancedPropertyGrid);
}
internal void RefreshText() {
Text = Path.GetFileNameWithoutExtension(ASFConfig.FilePath);
}
internal void RefreshText() => Text = Path.GetFileNameWithoutExtension(ASFConfig.FilePath);
}
}

View File

@@ -22,14 +22,16 @@
*/
using System.Diagnostics.CodeAnalysis;
namespace ConfigGenerator {
internal static class Debugging {
#if DEBUG
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal static readonly bool IsDebugBuild = true;
#else
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal static readonly bool IsDebugBuild = false;
#endif
internal static bool IsReleaseBuild => !IsDebugBuild;
}
}

View File

@@ -25,50 +25,53 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using ConfigGenerator.Properties;
namespace ConfigGenerator {
internal sealed class DialogBox {
internal static class DialogBox {
internal static DialogResult InputBox(string title, string promptText, out string value) {
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
value = null;
return DialogResult.Abort;
}
Form form = new Form();
Label label = new Label();
TextBox textBox = new TextBox();
TextBox textBox = new TextBox {
Anchor = AnchorStyles.Right,
Bounds = new Rectangle(12, 36, 372, 20),
Width = 1000
};
textBox.Width = 1000;
Button buttonOk = new Button();
Button buttonCancel = new Button();
Button buttonOk = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
Bounds = new Rectangle(228, 72, 75, 23),
DialogResult = DialogResult.OK,
Text = Resources.OK
};
form.Text = title;
label.Text = promptText;
Button buttonCancel = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
Bounds = new Rectangle(309, 72, 75, 23),
DialogResult = DialogResult.Cancel,
Text = Resources.Cancel
};
buttonOk.Text = "OK";
buttonCancel.Text = "Cancel";
buttonOk.DialogResult = DialogResult.OK;
buttonCancel.DialogResult = DialogResult.Cancel;
Label label = new Label {
AutoSize = true,
Bounds = new Rectangle(9, 20, 372, 13),
Text = promptText
};
label.SetBounds(9, 20, 372, 13);
textBox.SetBounds(12, 36, 372, 20);
buttonOk.SetBounds(228, 72, 75, 23);
buttonCancel.SetBounds(309, 72, 75, 23);
label.AutoSize = true;
textBox.Anchor = textBox.Anchor | AnchorStyles.Right;
buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
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;
Form form = new Form {
AcceptButton = buttonOk,
CancelButton = buttonCancel,
ClientSize = new Size(Math.Max(300, label.Right + 10), 107),
Controls = { label, textBox, buttonOk, buttonCancel },
FormBorderStyle = FormBorderStyle.FixedDialog,
MinimizeBox = false,
MaximizeBox = false,
StartPosition = FormStartPosition.CenterScreen,
Text = title
};
DialogResult dialogResult = form.ShowDialog();
value = textBox.Text;
@@ -80,37 +83,37 @@ namespace ConfigGenerator {
return DialogResult.Abort;
}
Form form = new Form();
Label label = new Label();
Button buttonYes = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
Bounds = new Rectangle(228, 72, 75, 23),
DialogResult = DialogResult.Yes,
Text = Resources.Yes
};
Button buttonOk = new Button();
Button buttonCancel = new Button();
Button buttonNo = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
Bounds = new Rectangle(309, 72, 75, 23),
DialogResult = DialogResult.No,
Text = Resources.No
};
form.Text = title;
label.Text = promptText;
Label label = new Label {
AutoSize = true,
Bounds = new Rectangle(9, 20, 372, 13),
Text = promptText
};
buttonOk.Text = "Yes";
buttonCancel.Text = "No";
buttonOk.DialogResult = DialogResult.Yes;
buttonCancel.DialogResult = DialogResult.No;
label.SetBounds(9, 20, 372, 13);
buttonOk.SetBounds(228, 50, 75, 23);
buttonCancel.SetBounds(309, 50, 75, 23);
label.AutoSize = true;
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;
Form form = new Form {
AcceptButton = buttonYes,
CancelButton = buttonNo,
ClientSize = new Size(Math.Max(300, label.Right + 10), 107),
Controls = { label, buttonYes, buttonNo },
FormBorderStyle = FormBorderStyle.FixedDialog,
MinimizeBox = false,
MaximizeBox = false,
StartPosition = FormStartPosition.CenterScreen,
Text = title
};
DialogResult dialogResult = form.ShowDialog();
return dialogResult;

View File

@@ -31,7 +31,7 @@ namespace ConfigGenerator {
internal EnhancedPropertyGrid(ASFConfig config) {
if (config == null) {
throw new ArgumentNullException("config");
throw new ArgumentNullException(nameof(config));
}
ASFConfig = config;
@@ -53,20 +53,24 @@ namespace ConfigGenerator {
BotConfig botConfig = ASFConfig as BotConfig;
if (botConfig != null) {
if (botConfig.Enabled) {
Tutorial.OnAction(Tutorial.EPhase.BotEnabled);
if (!string.IsNullOrEmpty(botConfig.SteamLogin) && !string.IsNullOrEmpty(botConfig.SteamPassword)) {
Tutorial.OnAction(Tutorial.EPhase.BotReady);
}
if (!botConfig.Enabled) {
return;
}
Tutorial.OnAction(Tutorial.EPhase.BotEnabled);
if (!string.IsNullOrEmpty(botConfig.SteamLogin) && !string.IsNullOrEmpty(botConfig.SteamPassword)) {
Tutorial.OnAction(Tutorial.EPhase.BotReady);
}
return;
}
GlobalConfig globalConfig = ASFConfig as GlobalConfig;
if (globalConfig != null) {
if (globalConfig.SteamOwnerID != 0) {
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigReady);
}
if (globalConfig == null) {
return;
}
if (globalConfig.SteamOwnerID != 0) {
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigReady);
}
}

View File

@@ -25,11 +25,14 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Sockets;
namespace ConfigGenerator {
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")]
internal sealed class GlobalConfig : ASFConfig {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal enum EUpdateChannel : byte {
Unknown,
Stable,
@@ -43,7 +46,7 @@ namespace ConfigGenerator {
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
// 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)]
public bool Debug { get; set; } = false;
@@ -102,7 +105,6 @@ namespace ConfigGenerator {
[JsonProperty(Required = Required.DisallowNull)]
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)]
public bool HackIgnoreMachineID { get; set; } = false;
@@ -161,20 +163,22 @@ namespace ConfigGenerator {
globalConfig.HttpTimeout = DefaultHttpTimeout;
}
if (globalConfig.WCFPort == 0) {
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
globalConfig.WCFPort = DefaultWCFPort;
if (globalConfig.WCFPort != 0) {
return globalConfig;
}
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
globalConfig.WCFPort = DefaultWCFPort;
return globalConfig;
}
// This constructor is used only by deserializer
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private GlobalConfig() { }
private GlobalConfig(string filePath) : base(filePath) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException("filePath");
throw new ArgumentNullException(nameof(filePath));
}
Blacklist.AddRange(GlobalBlacklist);

View File

@@ -23,9 +23,9 @@
*/
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
using ConfigGenerator.Properties;
namespace ConfigGenerator {
internal static class Logging {
@@ -34,60 +34,40 @@ namespace ConfigGenerator {
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)) {
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)) {
return;
}
MessageBox.Show(previousMethodName + "() " + message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
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);
MessageBox.Show(previousMethodName + @"() " + message, Resources.Warning, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}

View File

@@ -26,6 +26,7 @@ using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
@@ -33,9 +34,9 @@ namespace ConfigGenerator {
internal sealed partial class MainForm : Form {
private const byte ReservedTabs = 3;
private readonly TabPage NewTab = new TabPage { Text = "+" };
private readonly TabPage RemoveTab = new TabPage { Text = "-" };
private readonly TabPage RenameTab = new TabPage { Text = "~" };
private readonly TabPage NewTab = new TabPage { Text = @"+" };
private readonly TabPage RemoveTab = new TabPage { Text = @"-" };
private readonly TabPage RenameTab = new TabPage { Text = @"~" };
private ConfigPage ASFTab;
private TabPage OldTab;
@@ -45,7 +46,7 @@ namespace ConfigGenerator {
}
private void MainForm_Load(object sender, EventArgs e) {
if (sender == null || e == null) {
if ((sender == null) || (e == null)) {
return;
}
@@ -53,7 +54,7 @@ namespace ConfigGenerator {
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);
switch (botName) {
case Program.ASF:
@@ -71,7 +72,7 @@ namespace ConfigGenerator {
}
private void MainTab_Selected(object sender, TabControlEventArgs e) {
if (sender == null || e == null) {
if ((sender == null) || (e == null)) {
return;
}
@@ -151,11 +152,9 @@ namespace ConfigGenerator {
// Get rid of any potential whitespaces in bot name
input = Regex.Replace(input, @"\s+", "");
foreach (ASFConfig config in ASFConfig.ASFConfigs) {
if (Path.GetFileNameWithoutExtension(config.FilePath).Equals(input)) {
Logging.LogGenericError("Bot with such name exists already!");
return;
}
if (ASFConfig.ASFConfigs.Select(config => Path.GetFileNameWithoutExtension(config.FilePath)).Any(fileNameWithoutExtension => (fileNameWithoutExtension == null) || fileNameWithoutExtension.Equals(input))) {
Logging.LogGenericError("Bot with such name exists already!");
return;
}
input = Path.Combine(Program.ConfigDirectory, input + ".json");
@@ -170,7 +169,7 @@ namespace ConfigGenerator {
}
private void MainTab_Deselecting(object sender, TabControlCancelEventArgs e) {
if (sender == null || e == null) {
if ((sender == null) || (e == null)) {
return;
}
@@ -178,7 +177,7 @@ namespace ConfigGenerator {
}
private void MainForm_Shown(object sender, EventArgs e) {
if (sender == null || e == null) {
if ((sender == null) || (e == null)) {
return;
}
@@ -186,7 +185,7 @@ namespace ConfigGenerator {
}
private void MainForm_HelpButtonClicked(object sender, CancelEventArgs e) {
if (sender == null || e == null) {
if ((sender == null) || (e == null)) {
return;
}

View File

@@ -61,10 +61,12 @@ namespace ConfigGenerator {
// Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up
for (byte i = 0; i < 4; i++) {
Directory.SetCurrentDirectory("..");
if (Directory.Exists(ASFDirectory)) {
Directory.SetCurrentDirectory(ASFDirectory);
break;
if (!Directory.Exists(ASFDirectory)) {
continue;
}
Directory.SetCurrentDirectory(ASFDirectory);
break;
}
// If config directory doesn't exist after our adjustment, abort all of that
@@ -73,14 +75,16 @@ namespace ConfigGenerator {
}
}
if (!Directory.Exists(ConfigDirectory)) {
Logging.LogGenericError("Config directory could not be found!");
Environment.Exit(1);
if (Directory.Exists(ConfigDirectory)) {
return;
}
Logging.LogGenericError("Config directory could not be found!");
Environment.Exit(1);
}
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
if (sender == null || args == null) {
if ((sender == null) || (args == null)) {
return;
}
@@ -88,7 +92,7 @@ namespace ConfigGenerator {
}
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
if (sender == null || args == null) {
if ((sender == null) || (args == null)) {
return;
}

View File

@@ -1,8 +1,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
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
// associated with an assembly.
[assembly: AssemblyTitle("ConfigGenerator")]
@@ -14,8 +13,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 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
// 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
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
@@ -25,11 +24,11 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Minor Version
// Build Number
// 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:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]

View File

@@ -9,54 +9,127 @@
//------------------------------------------------------------------------------
namespace ConfigGenerator.Properties {
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if ((resourceMan == null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ConfigGenerator.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ConfigGenerator.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
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);
}
}
}
}

View File

@@ -114,4 +114,28 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</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>

View File

@@ -38,12 +38,12 @@ namespace ConfigGenerator {
GlobalConfigReady
}
internal static bool Enabled { get; set; } = true;
internal static bool Enabled { private get; set; } = true;
private static EPhase NextPhase = EPhase.Start;
internal static void OnAction(EPhase phase) {
if (!Enabled || phase != NextPhase) {
if (!Enabled || (phase != NextPhase)) {
return;
}