mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-21 16:58:37 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3da5d6afc | ||
|
|
84f33fcef4 | ||
|
|
22f0d423a3 | ||
|
|
f1d7609796 | ||
|
|
77386ecae5 | ||
|
|
4e86d21ef8 | ||
|
|
044fc87691 | ||
|
|
79fad62a4d | ||
|
|
6622d3b147 | ||
|
|
6b6d5429ad | ||
|
|
d59d0a8415 | ||
|
|
e3100d3938 | ||
|
|
42c020e552 | ||
|
|
554273833b | ||
|
|
093a29df62 | ||
|
|
31aa6b2e4a | ||
|
|
9f7ecdf054 | ||
|
|
7c4c74bf84 | ||
|
|
b8f03abd8b | ||
|
|
47f846540b | ||
|
|
bc14713079 | ||
|
|
770a8fee66 | ||
|
|
1df9af08e6 | ||
|
|
27464f6120 | ||
|
|
dfd45c6e25 | ||
|
|
adc1759cee | ||
|
|
88369ec71a | ||
|
|
a5d8ae53dd | ||
|
|
cd7b65868a | ||
|
|
7575704a01 | ||
|
|
b6ce8f435c | ||
|
|
565acca9fb | ||
|
|
610954ba73 | ||
|
|
74a748b03f | ||
|
|
585a075ec9 | ||
|
|
891d40afe1 | ||
|
|
387f0dd1c7 | ||
|
|
8b4d3c219c | ||
|
|
365877ec89 | ||
|
|
f03a43d573 | ||
|
|
d15a9cbfca | ||
|
|
acfad624fb |
@@ -1,4 +1,3 @@
|
|||||||
sudo: false
|
|
||||||
language: csharp
|
language: csharp
|
||||||
solution: ArchiSteamFarm.sln
|
solution: ArchiSteamFarm.sln
|
||||||
|
|
||||||
@@ -8,3 +7,6 @@ git:
|
|||||||
mono:
|
mono:
|
||||||
- weekly
|
- weekly
|
||||||
- latest
|
- latest
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly List<Notification> Notifications;
|
internal readonly HashSet<Notification> Notifications;
|
||||||
|
|
||||||
internal NotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
|
internal NotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
|
||||||
JobID = jobID;
|
JobID = jobID;
|
||||||
@@ -76,7 +76,7 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Notifications = new List<Notification>(msg.notifications.Count);
|
Notifications = new HashSet<Notification>();
|
||||||
foreach (var notification in msg.notifications) {
|
foreach (var notification in msg.notifications) {
|
||||||
Notifications.Add(new Notification((Notification.ENotificationType) notification.user_notification_type));
|
Notifications.Add(new Notification((Notification.ENotificationType) notification.user_notification_type));
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (msg.count_new_items > 0) {
|
if (msg.count_new_items > 0) {
|
||||||
Notifications = new List<Notification>(1) {
|
Notifications = new HashSet<Notification>() {
|
||||||
new Notification(Notification.ENotificationType.Items)
|
new Notification(Notification.ENotificationType.Items)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,6 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
internal sealed class OfflineMessageCallback : CallbackMsg {
|
internal sealed class OfflineMessageCallback : CallbackMsg {
|
||||||
internal readonly uint OfflineMessages;
|
internal readonly uint OfflineMessages;
|
||||||
internal readonly List<uint> Users;
|
|
||||||
|
|
||||||
internal OfflineMessageCallback(JobID jobID, CMsgClientOfflineMessageNotification msg) {
|
internal OfflineMessageCallback(JobID jobID, CMsgClientOfflineMessageNotification msg) {
|
||||||
JobID = jobID;
|
JobID = jobID;
|
||||||
@@ -109,7 +108,6 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OfflineMessages = msg.offline_messages;
|
OfflineMessages = msg.offline_messages;
|
||||||
Users = msg.friends_with_offline_messages;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,8 +153,8 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
foreach (KeyValue lineItem in lineItems) {
|
foreach (KeyValue lineItem in lineItems) {
|
||||||
uint appID = (uint) lineItem["PackageID"].AsUnsignedLong();
|
uint appID = (uint) lineItem["PackageID"].AsUnsignedLong();
|
||||||
string gameName = lineItem["ItemDescription"].AsString();
|
string gameName = lineItem["ItemDescription"].Value;
|
||||||
gameName = WebUtility.UrlDecode(gameName); // Apparently steam expects client to decode sent HTML
|
gameName = WebUtility.HtmlDecode(gameName); // Apparently steam expects client to decode sent HTML
|
||||||
Items.Add(appID, gameName);
|
Items.Add(appID, gameName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ using SteamKit2;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
@@ -54,6 +53,53 @@ namespace ArchiSteamFarm {
|
|||||||
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunity;
|
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static uint GetAppIDFromMarketHashName(string hashName) {
|
||||||
|
if (string.IsNullOrEmpty(hashName)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = hashName.IndexOf('-');
|
||||||
|
if (index < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint appID;
|
||||||
|
if (!uint.TryParse(hashName.Substring(0, index), out appID)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return appID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Steam.Item.EType GetItemType(string name) {
|
||||||
|
if (string.IsNullOrEmpty(name)) {
|
||||||
|
return Steam.Item.EType.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case "Booster Pack":
|
||||||
|
return Steam.Item.EType.BoosterPack;
|
||||||
|
case "Coupon":
|
||||||
|
return Steam.Item.EType.Coupon;
|
||||||
|
case "Gift":
|
||||||
|
return Steam.Item.EType.Gift;
|
||||||
|
case "Steam Gems":
|
||||||
|
return Steam.Item.EType.SteamGems;
|
||||||
|
default:
|
||||||
|
if (name.EndsWith("Emoticon", StringComparison.Ordinal)) {
|
||||||
|
return Steam.Item.EType.Emoticon;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal ArchiWebHandler(Bot bot) {
|
internal ArchiWebHandler(Bot bot) {
|
||||||
if (bot == null) {
|
if (bot == null) {
|
||||||
throw new ArgumentNullException("bot");
|
throw new ArgumentNullException("bot");
|
||||||
@@ -133,9 +179,11 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool?> IsLoggedIn() {
|
internal async Task<bool?> IsLoggedIn() {
|
||||||
|
string request = SteamCommunityURL + "/my/profile";
|
||||||
|
|
||||||
HtmlDocument htmlDocument = null;
|
HtmlDocument htmlDocument = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile").ConfigureAwait(false);
|
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htmlDocument == null) {
|
if (htmlDocument == null) {
|
||||||
@@ -148,15 +196,13 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> RefreshSessionIfNeeded() {
|
internal async Task<bool> RefreshSessionIfNeeded() {
|
||||||
DateTime now = DateTime.Now;
|
if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||||
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SessionSemaphore.WaitAsync().ConfigureAwait(false);
|
await SessionSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
now = DateTime.Now;
|
if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||||
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
|
||||||
SessionSemaphore.Release();
|
SessionSemaphore.Release();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -166,8 +212,7 @@ namespace ArchiSteamFarm {
|
|||||||
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
||||||
if (isLoggedIn.GetValueOrDefault(true)) {
|
if (isLoggedIn.GetValueOrDefault(true)) {
|
||||||
result = true;
|
result = true;
|
||||||
now = DateTime.Now;
|
LastSessionRefreshCheck = DateTime.Now;
|
||||||
LastSessionRefreshCheck = now;
|
|
||||||
} else {
|
} else {
|
||||||
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
|
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
|
||||||
result = await Bot.RefreshSession().ConfigureAwait(false);
|
result = await Bot.RefreshSession().ConfigureAwait(false);
|
||||||
@@ -222,7 +267,7 @@ namespace ArchiSteamFarm {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal List<Steam.TradeOffer> GetTradeOffers() {
|
internal HashSet<Steam.TradeOffer> GetTradeOffers() {
|
||||||
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -236,6 +281,7 @@ namespace ArchiSteamFarm {
|
|||||||
response = iEconService.GetTradeOffers(
|
response = iEconService.GetTradeOffers(
|
||||||
get_received_offers: 1,
|
get_received_offers: 1,
|
||||||
active_only: 1,
|
active_only: 1,
|
||||||
|
get_descriptions: 1,
|
||||||
secure: !Program.GlobalConfig.ForceHttp
|
secure: !Program.GlobalConfig.ForceHttp
|
||||||
);
|
);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -249,33 +295,87 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Steam.TradeOffer> result = new List<Steam.TradeOffer>();
|
Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>> descriptionMap = new Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>>();
|
||||||
|
foreach (KeyValue description in response["descriptions"].Children) {
|
||||||
|
ulong classID = description["classid"].AsUnsignedLong();
|
||||||
|
if (classID == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong instanceID = description["instanceid"].AsUnsignedLong();
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(classID, instanceID);
|
||||||
|
if (descriptionMap.ContainsKey(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint appID = 0;
|
||||||
|
Steam.Item.EType type = Steam.Item.EType.Unknown;
|
||||||
|
|
||||||
|
string hashName = description["market_hash_name"].Value;
|
||||||
|
if (!string.IsNullOrEmpty(hashName)) {
|
||||||
|
appID = GetAppIDFromMarketHashName(hashName);
|
||||||
|
}
|
||||||
|
|
||||||
|
string descriptionType = description["type"].Value;
|
||||||
|
if (!string.IsNullOrEmpty(descriptionType)) {
|
||||||
|
type = GetItemType(descriptionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionMap[key] = new Tuple<uint, Steam.Item.EType>(appID, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<Steam.TradeOffer> result = new HashSet<Steam.TradeOffer>();
|
||||||
foreach (KeyValue trade in response["trade_offers_received"].Children) {
|
foreach (KeyValue trade in response["trade_offers_received"].Children) {
|
||||||
|
// TODO: Correct some of these when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released
|
||||||
Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
|
Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
|
||||||
tradeofferid = trade["tradeofferid"].AsString(),
|
TradeOfferID = trade["tradeofferid"].AsUnsignedLong(),
|
||||||
accountid_other = (uint) trade["accountid_other"].AsUnsignedLong(), // TODO: Correct this when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released
|
OtherSteamID3 = (uint) trade["accountid_other"].AsUnsignedLong(),
|
||||||
trade_offer_state = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
|
State = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (KeyValue item in trade["items_to_give"].Children) {
|
foreach (KeyValue item in trade["items_to_give"].Children) {
|
||||||
tradeOffer.items_to_give.Add(new Steam.Item {
|
Steam.Item steamItem = new Steam.Item {
|
||||||
appid = item["appid"].AsString(),
|
AppID = (uint) item["appid"].AsUnsignedLong(),
|
||||||
contextid = item["contextid"].AsString(),
|
ContextID = item["contextid"].AsUnsignedLong(),
|
||||||
assetid = item["assetid"].AsString(),
|
AssetID = item["assetid"].AsUnsignedLong(),
|
||||||
classid = item["classid"].AsString(),
|
ClassID = item["classid"].AsUnsignedLong(),
|
||||||
instanceid = item["instanceid"].AsString(),
|
InstanceID = item["instanceid"].AsUnsignedLong(),
|
||||||
amount = item["amount"].AsString(),
|
Amount = (uint) item["amount"].AsUnsignedLong()
|
||||||
});
|
};
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
||||||
|
|
||||||
|
Tuple<uint, Steam.Item.EType> description;
|
||||||
|
if (descriptionMap.TryGetValue(key, out description)) {
|
||||||
|
steamItem.RealAppID = description.Item1;
|
||||||
|
steamItem.Type = description.Item2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tradeOffer.ItemsToGive.Add(steamItem);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (KeyValue item in trade["items_to_receive"].Children) {
|
foreach (KeyValue item in trade["items_to_receive"].Children) {
|
||||||
tradeOffer.items_to_receive.Add(new Steam.Item {
|
Steam.Item steamItem = new Steam.Item {
|
||||||
appid = item["appid"].AsString(),
|
AppID = (uint) item["appid"].AsUnsignedLong(),
|
||||||
contextid = item["contextid"].AsString(),
|
ContextID = item["contextid"].AsUnsignedLong(),
|
||||||
assetid = item["assetid"].AsString(),
|
AssetID = item["assetid"].AsUnsignedLong(),
|
||||||
classid = item["classid"].AsString(),
|
ClassID = item["classid"].AsUnsignedLong(),
|
||||||
instanceid = item["instanceid"].AsString(),
|
InstanceID = item["instanceid"].AsUnsignedLong(),
|
||||||
amount = item["amount"].AsString(),
|
Amount = (uint) item["amount"].AsUnsignedLong()
|
||||||
});
|
};
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
||||||
|
|
||||||
|
Tuple<uint, Steam.Item.EType> description;
|
||||||
|
if (descriptionMap.TryGetValue(key, out description)) {
|
||||||
|
steamItem.RealAppID = description.Item1;
|
||||||
|
steamItem.Type = description.Item2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tradeOffer.ItemsToReceive.Add(steamItem);
|
||||||
|
}
|
||||||
|
|
||||||
result.Add(tradeOffer);
|
result.Add(tradeOffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,14 +454,20 @@ namespace ArchiSteamFarm {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<List<Steam.Item>> GetMyTradableInventory() {
|
internal async Task<HashSet<Steam.Item>> GetMyTradableInventory() {
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HashSet<Steam.Item> result = new HashSet<Steam.Item>();
|
||||||
|
|
||||||
|
ushort nextPage = 0;
|
||||||
|
while (true) {
|
||||||
|
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1&start=" + nextPage;
|
||||||
|
|
||||||
JObject jObject = null;
|
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(SteamCommunityURL + "/my/inventory/json/753/6?trading=1").ConfigureAwait(false);
|
jObject = await WebBrowser.UrlGetToJObject(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jObject == null) {
|
if (jObject == null) {
|
||||||
@@ -369,25 +475,99 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<JToken> jTokens = jObject.SelectTokens("$.rgInventory.*");
|
IEnumerable<JToken> descriptions = jObject.SelectTokens("$.rgDescriptions.*");
|
||||||
if (jTokens == null) {
|
if (descriptions == null) {
|
||||||
Logging.LogNullError("jTokens", Bot.BotName);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Steam.Item> result = new List<Steam.Item>();
|
Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>> descriptionMap = new Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>>();
|
||||||
foreach (JToken jToken in jTokens) {
|
foreach (JToken description in descriptions) {
|
||||||
|
string classIDString = description["classid"].ToString();
|
||||||
|
if (string.IsNullOrEmpty(classIDString)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong classID;
|
||||||
|
if (!ulong.TryParse(classIDString, out classID) || classID == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string instanceIDString = description["instanceid"].ToString();
|
||||||
|
if (string.IsNullOrEmpty(instanceIDString)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong instanceID;
|
||||||
|
if (!ulong.TryParse(instanceIDString, out instanceID)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(classID, instanceID);
|
||||||
|
if (descriptionMap.ContainsKey(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint appID = 0;
|
||||||
|
Steam.Item.EType type = Steam.Item.EType.Unknown;
|
||||||
|
|
||||||
|
string hashName = description["market_hash_name"].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(hashName)) {
|
||||||
|
appID = GetAppIDFromMarketHashName(hashName);
|
||||||
|
}
|
||||||
|
|
||||||
|
string descriptionType = description["type"].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(descriptionType)) {
|
||||||
|
type = GetItemType(descriptionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionMap[key] = new Tuple<uint, Steam.Item.EType>(appID, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<JToken> items = jObject.SelectTokens("$.rgInventory.*");
|
||||||
|
if (descriptions == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (JToken item in items) {
|
||||||
|
|
||||||
|
Steam.Item steamItem;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result.Add(JsonConvert.DeserializeObject<Steam.Item>(jToken.ToString()));
|
steamItem = JsonConvert.DeserializeObject<Steam.Item>(item.ToString());
|
||||||
} catch (Exception e) {
|
} catch (JsonException e) {
|
||||||
Logging.LogGenericException(e, Bot.BotName);
|
Logging.LogGenericException(e, Bot.BotName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (steamItem == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
||||||
|
|
||||||
|
Tuple<uint, Steam.Item.EType> description;
|
||||||
|
if (descriptionMap.TryGetValue(key, out description)) {
|
||||||
|
steamItem.RealAppID = description.Item1;
|
||||||
|
steamItem.Type = description.Item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(steamItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool more;
|
||||||
|
if (!bool.TryParse(jObject["more"].ToString(), out more) || !more) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ushort.TryParse(jObject["more_start"].ToString(), out nextPage)) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> SendTradeOffer(List<Steam.Item> inventory, ulong partnerID, string token = null) {
|
internal async Task<bool> SendTradeOffer(HashSet<Steam.Item> inventory, ulong partnerID, string token = null) {
|
||||||
if (inventory == null || inventory.Count == 0 || partnerID == 0) {
|
if (inventory == null || inventory.Count == 0 || partnerID == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -402,26 +582,30 @@ namespace ArchiSteamFarm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Steam.TradeOfferRequest> trades = new List<Steam.TradeOfferRequest>(1 + inventory.Count / Trading.MaxItemsPerTrade);
|
HashSet<Steam.TradeOfferRequest> trades = new HashSet<Steam.TradeOfferRequest>();
|
||||||
|
|
||||||
Steam.TradeOfferRequest singleTrade = null;
|
Steam.TradeOfferRequest singleTrade = null;
|
||||||
for (ushort i = 0; i < inventory.Count; i++) {
|
|
||||||
if (i % Trading.MaxItemsPerTrade == 0) {
|
byte itemID = 0;
|
||||||
|
foreach (Steam.Item item in inventory) {
|
||||||
|
if (itemID % Trading.MaxItemsPerTrade == 0) {
|
||||||
if (trades.Count >= Trading.MaxTradesPerAccount) {
|
if (trades.Count >= Trading.MaxTradesPerAccount) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
singleTrade = new Steam.TradeOfferRequest();
|
singleTrade = new Steam.TradeOfferRequest();
|
||||||
trades.Add(singleTrade);
|
trades.Add(singleTrade);
|
||||||
|
itemID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Steam.Item item = inventory[i];
|
singleTrade.ItemsToGive.Assets.Add(new Steam.Item() {
|
||||||
singleTrade.me.assets.Add(new Steam.Item() {
|
AppID = Steam.Item.SteamAppID,
|
||||||
appid = "753",
|
ContextID = Steam.Item.SteamContextID,
|
||||||
contextid = "6",
|
Amount = item.Amount,
|
||||||
amount = item.amount,
|
AssetID = item.AssetID
|
||||||
assetid = item.id
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itemID++;
|
||||||
}
|
}
|
||||||
|
|
||||||
string referer = SteamCommunityURL + "/tradeoffer/new";
|
string referer = SteamCommunityURL + "/tradeoffer/new";
|
||||||
@@ -460,9 +644,11 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string request = SteamCommunityURL + "/my/badges?p=" + page;
|
||||||
|
|
||||||
HtmlDocument htmlDocument = null;
|
HtmlDocument htmlDocument = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/badges?p=" + page).ConfigureAwait(false);
|
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htmlDocument == null) {
|
if (htmlDocument == null) {
|
||||||
@@ -482,9 +668,11 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string request = SteamCommunityURL + "/my/gamecards/" + appID;
|
||||||
|
|
||||||
HtmlDocument htmlDocument = null;
|
HtmlDocument htmlDocument = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/gamecards/" + appID).ConfigureAwait(false);
|
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htmlDocument == null) {
|
if (htmlDocument == null) {
|
||||||
@@ -500,9 +688,11 @@ namespace ArchiSteamFarm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string request = SteamCommunityURL + "/my/inventory";
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||||
result = await WebBrowser.UrlGet(SteamCommunityURL + "/my/inventory").ConfigureAwait(false);
|
result = await WebBrowser.UrlGet(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ using System.Security.Cryptography;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal sealed class Bot {
|
internal sealed class Bot {
|
||||||
@@ -100,9 +101,8 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Steam keys are offered in many formats: https://support.steampowered.com/kb_article.php?ref=7480-WUSF-3601
|
// Steam keys are offered in many formats: https://support.steampowered.com/kb_article.php?ref=7480-WUSF-3601
|
||||||
// It's pointless to implement them all, so we'll just do a simple check if key is supposed to be valid
|
// This regex should catch all of them, we can always further extend it in future
|
||||||
// Every valid key, apart from Prey one has at least two dashes
|
return Regex.IsMatch(key, @"[0-9A-Z]{4,5}-[0-9A-Z]{4,5}-[0-9A-Z]{4,5}-?(?:(?:[0-9A-Z]{4,5}-?)?(?:[0-9A-Z]{4,5}))?");
|
||||||
return Utilities.GetCharCountInString(key, '-') >= 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Bot(string botName) {
|
internal Bot(string botName) {
|
||||||
@@ -295,8 +295,8 @@ namespace ArchiSteamFarm {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task OnFarmingFinished(bool farmedSomething) {
|
internal async Task OnFarmingFinished() {
|
||||||
if (farmedSomething && BotConfig.SendOnFarmingFinished) {
|
if (BotConfig.SendOnFarmingFinished) {
|
||||||
await ResponseSendTrade(BotConfig.SteamMasterID).ConfigureAwait(false);
|
await ResponseSendTrade(BotConfig.SteamMasterID).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,11 +310,11 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!message.StartsWith("!")) {
|
if (message[0] != '!') {
|
||||||
return await ResponseRedeem(steamID, message.Replace(",", Environment.NewLine), true).ConfigureAwait(false);
|
return await ResponseRedeem(steamID, message.Replace(",", Environment.NewLine), true).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!message.Contains(" ")) {
|
if (message.IndexOf(' ') < 0) {
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case "!2fa":
|
case "!2fa":
|
||||||
return Response2FA(steamID);
|
return Response2FA(steamID);
|
||||||
@@ -350,7 +350,7 @@ namespace ArchiSteamFarm {
|
|||||||
return ResponseUnknown(steamID);
|
return ResponseUnknown(steamID);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
string[] args = message.Split(' ');
|
string[] args = message.Split((char[]) null, StringSplitOptions.RemoveEmptyEntries);
|
||||||
switch (args[0]) {
|
switch (args[0]) {
|
||||||
case "!2fa":
|
case "!2fa":
|
||||||
return Response2FA(steamID, args[1]);
|
return Response2FA(steamID, args[1]);
|
||||||
@@ -410,8 +410,8 @@ namespace ArchiSteamFarm {
|
|||||||
Task.Run(() => HandleCallbacks()).Forget();
|
Task.Run(() => HandleCallbacks()).Forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2FA tokens are expiring soon, use limiter only when we don't have any pending
|
// 2FA tokens are expiring soon, use limiter only when user is providing one
|
||||||
if (TwoFactorCode == null) {
|
if (TwoFactorCode == null || BotDatabase.SteamGuardAccount != null) {
|
||||||
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
|
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,6 +463,7 @@ namespace ArchiSteamFarm {
|
|||||||
Logging.LogGenericInfo("ASF requires a few more steps to complete authenticator import...", BotName);
|
Logging.LogGenericInfo("ASF requires a few more steps to complete authenticator import...", BotName);
|
||||||
|
|
||||||
if (!InitializeLoginAndPassword()) {
|
if (!InitializeLoginAndPassword()) {
|
||||||
|
BotDatabase.SteamGuardAccount = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,10 +474,12 @@ namespace ArchiSteamFarm {
|
|||||||
case LoginResult.Need2FA:
|
case LoginResult.Need2FA:
|
||||||
userLogin.TwoFactorCode = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
|
userLogin.TwoFactorCode = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
|
||||||
if (string.IsNullOrEmpty(userLogin.TwoFactorCode)) {
|
if (string.IsNullOrEmpty(userLogin.TwoFactorCode)) {
|
||||||
|
BotDatabase.SteamGuardAccount = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
BotDatabase.SteamGuardAccount = null;
|
||||||
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
|
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -588,12 +591,18 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false);
|
await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false);
|
||||||
List<Steam.Item> inventory = await ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false);
|
HashSet<Steam.Item> inventory = await ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false);
|
||||||
|
|
||||||
if (inventory == null || inventory.Count == 0) {
|
if (inventory == null || inventory.Count == 0) {
|
||||||
return "Nothing to send, inventory seems empty!";
|
return "Nothing to send, inventory seems empty!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove from our pending inventory all items that are not steam cards and boosters
|
||||||
|
inventory.RemoveWhere(item => item.Type != Steam.Item.EType.TradingCard && item.Type != Steam.Item.EType.FoilTradingCard && item.Type != Steam.Item.EType.BoosterPack);
|
||||||
|
if (inventory.Count == 0) {
|
||||||
|
return "Nothing to send, inventory seems empty!";
|
||||||
|
}
|
||||||
|
|
||||||
if (await ArchiWebHandler.SendTradeOffer(inventory, BotConfig.SteamMasterID, BotConfig.SteamTradeToken).ConfigureAwait(false)) {
|
if (await ArchiWebHandler.SendTradeOffer(inventory, BotConfig.SteamMasterID, BotConfig.SteamTradeToken).ConfigureAwait(false)) {
|
||||||
await AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
await AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
||||||
return "Trade offer sent successfully!";
|
return "Trade offer sent successfully!";
|
||||||
@@ -774,12 +783,16 @@ namespace ArchiSteamFarm {
|
|||||||
while (!string.IsNullOrEmpty(key) && currentBot != null) {
|
while (!string.IsNullOrEmpty(key) && currentBot != null) {
|
||||||
if (validate && !IsValidCdKey(key)) {
|
if (validate && !IsValidCdKey(key)) {
|
||||||
key = reader.ReadLine(); // Next key
|
key = reader.ReadLine(); // Next key
|
||||||
continue; // Without changing the bot
|
continue; // Keep current bot
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentBot.SteamClient.IsConnected) {
|
if (!currentBot.SteamClient.IsConnected) {
|
||||||
|
currentBot = null; // Either bot will be changed, or loop aborted
|
||||||
|
} else {
|
||||||
ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
||||||
if (result != null) {
|
if (result == null) {
|
||||||
|
currentBot = null; // Either bot will be changed, or loop aborted
|
||||||
|
} else {
|
||||||
switch (result.PurchaseResult) {
|
switch (result.PurchaseResult) {
|
||||||
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
|
||||||
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
|
||||||
@@ -787,7 +800,12 @@ namespace ArchiSteamFarm {
|
|||||||
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + result.PurchaseResult + " | Items: " + string.Join("", result.Items));
|
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + result.PurchaseResult + " | Items: " + string.Join("", result.Items));
|
||||||
|
|
||||||
key = reader.ReadLine(); // Next key
|
key = reader.ReadLine(); // Next key
|
||||||
|
|
||||||
|
if (result.PurchaseResult == ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK) {
|
||||||
break; // Next bot (if needed)
|
break; // Next bot (if needed)
|
||||||
|
} else {
|
||||||
|
continue; // Keep current bot
|
||||||
|
}
|
||||||
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.AlreadyOwned:
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.AlreadyOwned:
|
||||||
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.BaseGameRequired:
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.BaseGameRequired:
|
||||||
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OnCooldown:
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OnCooldown:
|
||||||
@@ -1226,15 +1244,28 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
string sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName);
|
string sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName);
|
||||||
if (string.IsNullOrEmpty(sms)) {
|
if (string.IsNullOrEmpty(sms)) {
|
||||||
|
Logging.LogGenericWarning("Aborted!", BotName);
|
||||||
|
DelinkMobileAuthenticator();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticatorLinker.FinalizeResult finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(sms);
|
AuthenticatorLinker.FinalizeResult finalizeResult;
|
||||||
if (finalizeResult != AuthenticatorLinker.FinalizeResult.Success) {
|
while ((finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(sms)) != AuthenticatorLinker.FinalizeResult.Success) {
|
||||||
|
switch (finalizeResult) {
|
||||||
|
case AuthenticatorLinker.FinalizeResult.BadSMSCode:
|
||||||
|
sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName);
|
||||||
|
if (string.IsNullOrEmpty(sms)) {
|
||||||
|
Logging.LogGenericWarning("Aborted!", BotName);
|
||||||
|
DelinkMobileAuthenticator();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
|
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
|
||||||
DelinkMobileAuthenticator();
|
DelinkMobileAuthenticator();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure that we also save changes made by finalization step (if any)
|
// Ensure that we also save changes made by finalization step (if any)
|
||||||
BotDatabase.Save();
|
BotDatabase.Save();
|
||||||
@@ -1248,13 +1279,13 @@ namespace ArchiSteamFarm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool result = BotDatabase.SteamGuardAccount.DeactivateAuthenticator();
|
// Try to deactivate authenticator, and assume we're safe to remove if it wasn't fully enrolled yet (even if request fails)
|
||||||
|
if (BotDatabase.SteamGuardAccount.DeactivateAuthenticator() || !BotDatabase.SteamGuardAccount.FullyEnrolled) {
|
||||||
if (result) {
|
|
||||||
BotDatabase.SteamGuardAccount = null;
|
BotDatabase.SteamGuardAccount = null;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void JoinMasterChat() {
|
private void JoinMasterChat() {
|
||||||
@@ -1364,10 +1395,6 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!KeepRunning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (InvalidPassword) {
|
if (InvalidPassword) {
|
||||||
InvalidPassword = false;
|
InvalidPassword = false;
|
||||||
if (!string.IsNullOrEmpty(BotDatabase.LoginKey)) { // InvalidPassword means usually that login key has expired, if we used it
|
if (!string.IsNullOrEmpty(BotDatabase.LoginKey)) { // InvalidPassword means usually that login key has expired, if we used it
|
||||||
@@ -1389,10 +1416,14 @@ namespace ArchiSteamFarm {
|
|||||||
await Utilities.SleepAsync(Program.GlobalConfig.AccountPlayingDelay * 60 * 1000).ConfigureAwait(false);
|
await Utilities.SleepAsync(Program.GlobalConfig.AccountPlayingDelay * 60 * 1000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!KeepRunning || SteamClient.IsConnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Logging.LogGenericInfo("Reconnecting...", BotName);
|
Logging.LogGenericInfo("Reconnecting...", BotName);
|
||||||
|
|
||||||
// 2FA tokens are expiring soon, use limiter only when we don't have any pending
|
// 2FA tokens are expiring soon, use limiter only when user is providing one
|
||||||
if (TwoFactorCode == null) {
|
if (TwoFactorCode == null || BotDatabase.SteamGuardAccount != null) {
|
||||||
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
|
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1714,7 +1745,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void OnOfflineMessage(ArchiHandler.OfflineMessageCallback callback) {
|
private void OnOfflineMessage(ArchiHandler.OfflineMessageCallback callback) {
|
||||||
if (callback == null) {
|
if (callback == null || callback.OfflineMessages == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ namespace ArchiSteamFarm {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal bool AcceptGifts { get; private set; } = false;
|
internal bool AcceptGifts { get; private set; } = false;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
internal bool SteamTradeMatcher { get; private set; } = false;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal bool ForwardKeysToOtherBots { get; private set; } = false;
|
internal bool ForwardKeysToOtherBots { get; private set; } = false;
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace ArchiSteamFarm {
|
|||||||
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
|
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
|
||||||
internal readonly HashSet<uint> CurrentGamesFarming = new HashSet<uint>();
|
internal readonly HashSet<uint> CurrentGamesFarming = new HashSet<uint>();
|
||||||
|
|
||||||
private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false);
|
private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
|
||||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
||||||
private readonly Bot Bot;
|
private readonly Bot Bot;
|
||||||
private readonly Timer Timer;
|
private readonly Timer Timer;
|
||||||
@@ -94,7 +94,7 @@ namespace ArchiSteamFarm {
|
|||||||
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
|
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
|
||||||
Semaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
Semaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
||||||
Logging.LogGenericInfo("We don't have anything to farm on this account!", Bot.BotName);
|
Logging.LogGenericInfo("We don't have anything to farm on this account!", Bot.BotName);
|
||||||
await Bot.OnFarmingFinished(false).ConfigureAwait(false);
|
await Bot.OnFarmingFinished().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +102,6 @@ namespace ArchiSteamFarm {
|
|||||||
NowFarming = true;
|
NowFarming = true;
|
||||||
Semaphore.Release(); // From this point we allow other calls to shut us down
|
Semaphore.Release(); // From this point we allow other calls to shut us down
|
||||||
|
|
||||||
bool farmedSomething = false;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Now the algorithm used for farming depends on whether account is restricted or not
|
// Now the algorithm used for farming depends on whether account is restricted or not
|
||||||
if (Bot.BotConfig.CardDropsRestricted) { // If we have restricted card drops, we use complex algorithm
|
if (Bot.BotConfig.CardDropsRestricted) { // If we have restricted card drops, we use complex algorithm
|
||||||
@@ -114,7 +112,6 @@ namespace ArchiSteamFarm {
|
|||||||
while (gamesToFarmSolo.Count > 0) {
|
while (gamesToFarmSolo.Count > 0) {
|
||||||
uint appID = gamesToFarmSolo.First();
|
uint appID = gamesToFarmSolo.First();
|
||||||
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
||||||
farmedSomething = true;
|
|
||||||
gamesToFarmSolo.Remove(appID);
|
gamesToFarmSolo.Remove(appID);
|
||||||
gamesToFarmSolo.TrimExcess();
|
gamesToFarmSolo.TrimExcess();
|
||||||
} else {
|
} else {
|
||||||
@@ -135,9 +132,7 @@ namespace ArchiSteamFarm {
|
|||||||
Logging.LogGenericInfo("Chosen farming algorithm: Simple", Bot.BotName);
|
Logging.LogGenericInfo("Chosen farming algorithm: Simple", Bot.BotName);
|
||||||
while (GamesToFarm.Count > 0) {
|
while (GamesToFarm.Count > 0) {
|
||||||
uint appID = GamesToFarm.Keys.FirstOrDefault();
|
uint appID = GamesToFarm.Keys.FirstOrDefault();
|
||||||
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
if (!await FarmSolo(appID).ConfigureAwait(false)) {
|
||||||
farmedSomething = true;
|
|
||||||
} else {
|
|
||||||
NowFarming = false;
|
NowFarming = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -150,7 +145,7 @@ namespace ArchiSteamFarm {
|
|||||||
NowFarming = false;
|
NowFarming = false;
|
||||||
|
|
||||||
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
|
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
|
||||||
await Bot.OnFarmingFinished(farmedSomething).ConfigureAwait(false);
|
await Bot.OnFarmingFinished().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task StopFarming() {
|
internal async Task StopFarming() {
|
||||||
@@ -421,7 +416,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||||
for (ushort farmingTime = 0; farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
|
for (ushort farmingTime = 0; farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
|
||||||
if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||||
success = false;
|
success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -452,7 +447,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
while (maxHour < 2) {
|
while (maxHour < 2) {
|
||||||
if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||||
success = false;
|
success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ namespace ArchiSteamFarm {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal bool AutoUpdates { get; private set; } = true;
|
internal bool AutoUpdates { get; private set; } = true;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
internal bool AutoRestart { get; private set; } = true;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable;
|
internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable;
|
||||||
|
|
||||||
|
|||||||
@@ -28,40 +28,161 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal static class Steam {
|
internal static class Steam {
|
||||||
internal sealed class Item {
|
internal sealed class Item { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
|
||||||
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
|
internal const ushort SteamAppID = 753;
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
internal const byte SteamContextID = 6;
|
||||||
internal string appid { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
internal enum EType : byte {
|
||||||
internal string contextid { get; set; }
|
Unknown,
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
BoosterPack,
|
||||||
internal string assetid { get; set; }
|
Coupon,
|
||||||
|
Gift,
|
||||||
|
SteamGems,
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
Emoticon,
|
||||||
internal string id {
|
FoilTradingCard,
|
||||||
get { return assetid; }
|
ProfileBackground,
|
||||||
set { assetid = value; }
|
TradingCard
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.AllowNull)]
|
internal uint AppID;
|
||||||
internal string classid { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(Required = Required.AllowNull)]
|
[JsonProperty(PropertyName = "appid", Required = Required.DisallowNull)]
|
||||||
internal string instanceid { get; set; }
|
internal string AppIDString {
|
||||||
|
get {
|
||||||
[JsonProperty(Required = Required.Always)]
|
return AppID.ToString();
|
||||||
internal string amount { get; set; }
|
}
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class ItemList {
|
uint result;
|
||||||
[JsonProperty(Required = Required.Always)]
|
if (!uint.TryParse(value, out result)) {
|
||||||
internal List<Steam.Item> assets { get; } = new List<Steam.Item>();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class TradeOffer {
|
AppID = result;
|
||||||
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong ContextID;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "contextid", Required = Required.DisallowNull)]
|
||||||
|
internal string ContextIDString {
|
||||||
|
get {
|
||||||
|
return ContextID.ToString();
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong result;
|
||||||
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextID = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong AssetID;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "assetid", Required = Required.DisallowNull)]
|
||||||
|
internal string AssetIDString {
|
||||||
|
get {
|
||||||
|
return AssetID.ToString();
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong result;
|
||||||
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetID = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "id", Required = Required.DisallowNull)]
|
||||||
|
internal string ID {
|
||||||
|
get { return AssetIDString; }
|
||||||
|
set { AssetIDString = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong ClassID;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "classid", Required = Required.DisallowNull)]
|
||||||
|
internal string ClassIDString {
|
||||||
|
get {
|
||||||
|
return ClassID.ToString();
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong result;
|
||||||
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassID = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong InstanceID;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull)]
|
||||||
|
internal string InstanceIDString {
|
||||||
|
get {
|
||||||
|
return InstanceID.ToString();
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong result;
|
||||||
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceID = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal uint Amount;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "amount", Required = Required.Always)]
|
||||||
|
internal string AmountString {
|
||||||
|
get {
|
||||||
|
return Amount.ToString();
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint result;
|
||||||
|
if (!uint.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Amount = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal uint RealAppID { get; set; }
|
||||||
|
internal EType Type { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class TradeOffer { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
|
||||||
internal enum ETradeOfferState : byte {
|
internal enum ETradeOfferState : byte {
|
||||||
Unknown,
|
Unknown,
|
||||||
Invalid,
|
Invalid,
|
||||||
@@ -77,46 +198,148 @@ namespace ArchiSteamFarm {
|
|||||||
OnHold
|
OnHold
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
internal ulong TradeOfferID;
|
||||||
internal string tradeofferid { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(PropertyName = "tradeofferid", Required = Required.Always)]
|
||||||
internal uint accountid_other { get; set; }
|
internal string TradeOfferIDString {
|
||||||
|
get {
|
||||||
|
return TradeOfferID.ToString();
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
ulong result;
|
||||||
internal ETradeOfferState trade_offer_state { get; set; }
|
if (!ulong.TryParse(value, out result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
TradeOfferID = result;
|
||||||
internal List<Steam.Item> items_to_give { get; } = new List<Steam.Item>();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(PropertyName = "accountid_other", Required = Required.Always)]
|
||||||
internal List<Steam.Item> items_to_receive { get; } = new List<Steam.Item>();
|
internal uint OtherSteamID3 { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "trade_offer_state", Required = Required.Always)]
|
||||||
|
internal ETradeOfferState State { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "items_to_give", Required = Required.Always)]
|
||||||
|
internal HashSet<Item> ItemsToGive { get; } = new HashSet<Item>();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "items_to_receive", Required = Required.Always)]
|
||||||
|
internal HashSet<Item> ItemsToReceive { get; } = new HashSet<Item>();
|
||||||
|
|
||||||
// Extra
|
// Extra
|
||||||
private ulong _OtherSteamID64 = 0;
|
|
||||||
internal ulong OtherSteamID64 {
|
internal ulong OtherSteamID64 {
|
||||||
get {
|
get {
|
||||||
if (_OtherSteamID64 == 0 && accountid_other != 0) {
|
if (OtherSteamID3 == 0) {
|
||||||
_OtherSteamID64 = new SteamID(accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64();
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _OtherSteamID64;
|
return new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
|
||||||
}
|
}
|
||||||
|
set {
|
||||||
|
if (value == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OtherSteamID3 = new SteamID(value).AccountID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool IsSteamCardsOnlyTrade() {
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
uint amount;
|
||||||
|
if (ItemsPerType.TryGetValue(item.Type, out amount)) {
|
||||||
|
ItemsPerType[item.Type] = amount + item.Amount;
|
||||||
|
} else {
|
||||||
|
ItemsPerType[item.Type] = item.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<uint, Dictionary<Item.EType, uint>> ItemsToReceivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
|
||||||
|
foreach (Item item in ItemsToReceive) {
|
||||||
|
Dictionary<Item.EType, uint> ItemsPerType;
|
||||||
|
if (!ItemsToReceivePerGame.TryGetValue(item.RealAppID, out ItemsPerType)) {
|
||||||
|
ItemsPerType = new Dictionary<Item.EType, uint>();
|
||||||
|
ItemsPerType[item.Type] = item.Amount;
|
||||||
|
ItemsToReceivePerGame[item.RealAppID] = ItemsPerType;
|
||||||
|
} else {
|
||||||
|
uint amount;
|
||||||
|
if (ItemsPerType.TryGetValue(item.Type, out amount)) {
|
||||||
|
ItemsPerType[item.Type] = amount + item.Amount;
|
||||||
|
} else {
|
||||||
|
ItemsPerType[item.Type] = item.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that amount per type and per game matches
|
||||||
|
foreach (KeyValuePair<uint, Dictionary<Item.EType, uint>> ItemsPerGame in ItemsToGivePerGame) {
|
||||||
|
Dictionary<Item.EType, uint> otherItemsPerType;
|
||||||
|
if (!ItemsToReceivePerGame.TryGetValue(ItemsPerGame.Key, out otherItemsPerType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<Item.EType, uint> ItemsPerType in ItemsPerGame.Value) {
|
||||||
|
uint otherAmount;
|
||||||
|
if (!otherItemsPerType.TryGetValue(ItemsPerType.Key, out otherAmount)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ItemsPerType.Value != otherAmount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class TradeOfferRequest {
|
internal sealed class TradeOfferRequest {
|
||||||
[JsonProperty(Required = Required.Always)]
|
internal sealed class ItemList {
|
||||||
internal bool newversion { get; } = true;
|
[JsonProperty(PropertyName = "assets", Required = Required.Always)]
|
||||||
|
internal HashSet<Item> Assets { get; } = new HashSet<Item>();
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(PropertyName = "newversion", Required = Required.Always)]
|
||||||
internal int version { get; } = 2;
|
internal bool NewVersion { get; } = true;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(PropertyName = "version", Required = Required.Always)]
|
||||||
internal Steam.ItemList me { get; } = new Steam.ItemList();
|
internal byte Version { get; } = 2;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(PropertyName = "me", Required = Required.Always)]
|
||||||
internal Steam.ItemList them { get; } = new Steam.ItemList();
|
internal ItemList ItemsToGive { get; } = new ItemList();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "them", Required = Required.Always)]
|
||||||
|
internal ItemList ItemsToReceive { get; } = new ItemList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,13 +65,12 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only
|
private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only
|
||||||
|
|
||||||
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
|
internal static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
|
||||||
internal static readonly Version Version = Assembly.GetName().Version;
|
|
||||||
|
|
||||||
private static readonly object ConsoleLock = new object();
|
private static readonly object ConsoleLock = new object();
|
||||||
private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1);
|
private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1);
|
||||||
private static readonly ManualResetEvent ShutdownResetEvent = new ManualResetEvent(false);
|
private static readonly ManualResetEventSlim ShutdownResetEvent = new ManualResetEventSlim(false);
|
||||||
private static readonly string ExecutableFile = Assembly.Location;
|
private static readonly string ExecutableFile = Assembly.GetEntryAssembly().Location;
|
||||||
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
|
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
|
||||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
|
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
|
||||||
private static readonly WCF WCF = new WCF();
|
private static readonly WCF WCF = new WCF();
|
||||||
@@ -187,7 +186,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
GitHub.Asset binaryAsset = null;
|
GitHub.Asset binaryAsset = null;
|
||||||
foreach (var asset in releaseResponse.Assets) {
|
foreach (var asset in releaseResponse.Assets) {
|
||||||
if (string.IsNullOrEmpty(asset.Name) || !asset.Name.Equals(ExecutableName)) {
|
if (string.IsNullOrEmpty(asset.Name) || !asset.Name.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,10 +250,17 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.LogGenericInfo("Update process is finished! ASF will now restart itself...");
|
Logging.LogGenericInfo("Update process finished!");
|
||||||
await Utilities.SleepAsync(5000);
|
|
||||||
|
|
||||||
|
if (GlobalConfig.AutoRestart) {
|
||||||
|
Logging.LogGenericInfo("Restarting...");
|
||||||
|
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||||
Restart();
|
Restart();
|
||||||
|
} else {
|
||||||
|
Logging.LogGenericInfo("Exiting...");
|
||||||
|
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||||
|
Exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Exit(int exitCode = 0) {
|
internal static void Exit(int exitCode = 0) {
|
||||||
@@ -516,7 +522,7 @@ namespace ArchiSteamFarm {
|
|||||||
Init(args);
|
Init(args);
|
||||||
|
|
||||||
// Wait for signal to shutdown
|
// Wait for signal to shutdown
|
||||||
ShutdownResetEvent.WaitOne();
|
ShutdownResetEvent.Wait();
|
||||||
|
|
||||||
// We got a signal to shutdown
|
// We got a signal to shutdown
|
||||||
Exit();
|
Exit();
|
||||||
|
|||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("2.0.3.1")]
|
[assembly: AssemblyVersion("2.0.4.0")]
|
||||||
[assembly: AssemblyFileVersion("2.0.3.1")]
|
[assembly: AssemblyFileVersion("2.0.4.0")]
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
private readonly Bot Bot;
|
private readonly Bot Bot;
|
||||||
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1);
|
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1);
|
||||||
|
private readonly HashSet<ulong> RecentlyParsedTrades = new HashSet<ulong>();
|
||||||
|
|
||||||
private byte ParsingTasks;
|
private byte ParsingTasks;
|
||||||
|
|
||||||
@@ -79,43 +80,62 @@ namespace ArchiSteamFarm {
|
|||||||
TradesSemaphore.Release();
|
TradesSemaphore.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ForgetRecentTrade(ulong tradeID) {
|
||||||
|
await Utilities.SleepAsync(24 * 60 * 60 * 1000).ConfigureAwait(false);
|
||||||
|
lock (RecentlyParsedTrades) {
|
||||||
|
RecentlyParsedTrades.Remove(tradeID);
|
||||||
|
RecentlyParsedTrades.TrimExcess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ParseActiveTrades() {
|
private async Task ParseActiveTrades() {
|
||||||
List<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
|
HashSet<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
|
||||||
if (tradeOffers == null) {
|
if (tradeOffers == null || tradeOffers.Count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (RecentlyParsedTrades) {
|
||||||
|
tradeOffers.RemoveWhere(trade => RecentlyParsedTrades.Contains(trade.TradeOfferID));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tradeOffers.Count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Steam.TradeOffer tradeOffer in tradeOffers) {
|
||||||
|
lock (RecentlyParsedTrades) {
|
||||||
|
RecentlyParsedTrades.Add(tradeOffer.TradeOfferID);
|
||||||
|
}
|
||||||
|
|
||||||
|
ForgetRecentTrade(tradeOffer.TradeOfferID).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
|
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
|
||||||
await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
|
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
|
||||||
if (tradeOffer == null || tradeOffer.trade_offer_state != Steam.TradeOffer.ETradeOfferState.Active) {
|
if (tradeOffer == null || tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong tradeID;
|
if (await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false)) {
|
||||||
if (!ulong.TryParse(tradeOffer.tradeofferid, out tradeID)) {
|
Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||||
return;
|
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
|
||||||
}
|
|
||||||
|
|
||||||
if (ShouldAcceptTrade(tradeOffer)) {
|
|
||||||
Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
|
|
||||||
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
|
|
||||||
} else {
|
} else {
|
||||||
Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName);
|
Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ShouldAcceptTrade(Steam.TradeOffer tradeOffer) {
|
private async Task<bool> ShouldAcceptTrade(Steam.TradeOffer tradeOffer) {
|
||||||
if (tradeOffer == null) {
|
if (tradeOffer == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always accept trades when we're not losing anything
|
// Always accept trades when we're not losing anything
|
||||||
if (tradeOffer.items_to_give.Count == 0) {
|
if (tradeOffer.ItemsToGive.Count == 0) {
|
||||||
// Unless it's steam fuckup and we're dealing with broken trade
|
// Unless it's steam fuckup and we're dealing with broken trade
|
||||||
return tradeOffer.items_to_receive.Count > 0;
|
return tradeOffer.ItemsToReceive.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always accept trades from SteamMasterID
|
// Always accept trades from SteamMasterID
|
||||||
@@ -123,10 +143,84 @@ namespace ArchiSteamFarm {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add optional SteamTradeMatcher integration here
|
// If we don't have SteamTradeMatcher enabled, this is the end for us
|
||||||
|
if (!Bot.BotConfig.SteamTradeMatcher) {
|
||||||
// If no rule above matched this trade, reject it
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rule 1 - We always trade the same amount of items
|
||||||
|
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()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we're sure that STM trade is valid
|
||||||
|
// Now check if it's worth for us to do the trade
|
||||||
|
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false);
|
||||||
|
if (inventory == null || inventory.Count == 0) {
|
||||||
|
return true; // OK, assume that this trade is valid, we can't check our EQ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get appIDs we're interested in
|
||||||
|
HashSet<uint> appIDs = new HashSet<uint>();
|
||||||
|
foreach (Steam.Item item in tradeOffer.ItemsToGive) {
|
||||||
|
appIDs.Add(item.RealAppID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now remove from our inventory all items we're NOT interested in
|
||||||
|
inventory.RemoveWhere(item => !appIDs.Contains(item.RealAppID));
|
||||||
|
|
||||||
|
// If for some reason Valve is talking crap and we can't find mentioned items, assume OK
|
||||||
|
if (inventory.Count == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now let's create a map which maps items to their amount in our EQ
|
||||||
|
Dictionary<Tuple<ulong, ulong>, uint> amountMap = new Dictionary<Tuple<ulong, ulong>, uint>();
|
||||||
|
foreach (Steam.Item item in inventory) {
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(item.ClassID, item.InstanceID);
|
||||||
|
|
||||||
|
uint amount;
|
||||||
|
if (amountMap.TryGetValue(key, out amount)) {
|
||||||
|
amountMap[key] = amount + item.Amount;
|
||||||
|
} else {
|
||||||
|
amountMap[key] = item.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate our value of items to give
|
||||||
|
uint itemsToGiveDupesValue = 0;
|
||||||
|
foreach (Steam.Item item in tradeOffer.ItemsToGive) {
|
||||||
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(item.ClassID, item.InstanceID);
|
||||||
|
|
||||||
|
uint amount;
|
||||||
|
if (!amountMap.TryGetValue(key, out amount)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsToGiveDupesValue += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
uint amount;
|
||||||
|
if (!amountMap.TryGetValue(key, out amount)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsToReceiveDupesValue += 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ namespace ArchiSteamFarm {
|
|||||||
internal static void Forget(this Task task) { }
|
internal static void Forget(this Task task) { }
|
||||||
|
|
||||||
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
|
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
|
||||||
|
if (action == null) {
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
return Task.WhenAll(sequence.Select(action));
|
return Task.WhenAll(sequence.Select(action));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,20 +68,5 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
return Task.Delay(miliseconds);
|
return Task.Delay(miliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static uint GetCharCountInString(string s, char c) {
|
|
||||||
if (string.IsNullOrEmpty(s)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint count = 0;
|
|
||||||
foreach (char singleChar in s) {
|
|
||||||
if (singleChar == c) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,10 +141,8 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
content = WebUtility.HtmlDecode(content);
|
|
||||||
HtmlDocument htmlDocument = new HtmlDocument();
|
HtmlDocument htmlDocument = new HtmlDocument();
|
||||||
htmlDocument.LoadHtml(content);
|
htmlDocument.LoadHtml(WebUtility.HtmlDecode(content));
|
||||||
|
|
||||||
return htmlDocument;
|
return htmlDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +211,7 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.StartsWith("https://") && Program.GlobalConfig.ForceHttp) {
|
if (request.StartsWith("https://", StringComparison.Ordinal) && Program.GlobalConfig.ForceHttp) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,15 +237,17 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseMessage == null || !responseMessage.IsSuccessStatusCode) {
|
if (responseMessage == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!responseMessage.IsSuccessStatusCode) {
|
||||||
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
||||||
Logging.LogGenericError("Request: " + request + " failed!", Identifier);
|
Logging.LogGenericError("Request: " + request + " failed!", Identifier);
|
||||||
if (responseMessage != null) {
|
|
||||||
Logging.LogGenericError("Status code: " + responseMessage.StatusCode, Identifier);
|
Logging.LogGenericError("Status code: " + responseMessage.StatusCode, Identifier);
|
||||||
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false), Identifier);
|
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false), Identifier);
|
||||||
|
}
|
||||||
responseMessage.Dispose();
|
responseMessage.Dispose();
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"Debug": false,
|
"Debug": false,
|
||||||
"Headless": false,
|
"Headless": false,
|
||||||
"AutoUpdates": true,
|
"AutoUpdates": true,
|
||||||
|
"AutoRestart": true,
|
||||||
"UpdateChannel": 1,
|
"UpdateChannel": 1,
|
||||||
"SteamProtocol": 6,
|
"SteamProtocol": 6,
|
||||||
"SteamOwnerID": 0,
|
"SteamOwnerID": 0,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"FarmOffline": false,
|
"FarmOffline": false,
|
||||||
"HandleOfflineMessages": false,
|
"HandleOfflineMessages": false,
|
||||||
"AcceptGifts": false,
|
"AcceptGifts": false,
|
||||||
|
"SteamTradeMatcher": false,
|
||||||
"ForwardKeysToOtherBots": false,
|
"ForwardKeysToOtherBots": false,
|
||||||
"DistributeKeys": false,
|
"DistributeKeys": false,
|
||||||
"UseAsfAsMobileAuthenticator": false,
|
"UseAsfAsMobileAuthenticator": false,
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ namespace ConfigGenerator {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool AcceptGifts { get; set; } = false;
|
public bool AcceptGifts { get; set; } = false;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
public bool SteamTradeMatcher { get; set; } = false;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool ForwardKeysToOtherBots { get; set; } = false;
|
public bool ForwardKeysToOtherBots { get; set; } = false;
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ namespace ConfigGenerator {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool AutoUpdates { get; set; } = true;
|
public bool AutoUpdates { get; set; } = true;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
public bool AutoRestart { get; set; } = true;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public EUpdateChannel UpdateChannel { get; set; } = EUpdateChannel.Stable;
|
public EUpdateChannel UpdateChannel { get; set; } = EUpdateChannel.Stable;
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace ConfigGenerator {
|
|||||||
|
|
||||||
private const string ASFDirectory = "ArchiSteamFarm";
|
private const string ASFDirectory = "ArchiSteamFarm";
|
||||||
|
|
||||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
private static readonly string ExecutableDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The main entry point for the application.
|
/// The main entry point for the application.
|
||||||
|
|||||||
@@ -135,6 +135,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<!--
|
||||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||||
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||||
del "$(SolutionDir)out\ASF-GUI.exe.config"
|
del "$(SolutionDir)out\ASF-GUI.exe.config"
|
||||||
@@ -143,6 +144,7 @@
|
|||||||
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||||
rm "$(SolutionDir)out/ASF-GUI.exe.config"
|
rm "$(SolutionDir)out/ASF-GUI.exe.config"
|
||||||
</PostBuildEvent>
|
</PostBuildEvent>
|
||||||
|
-->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
|||||||
9
appveyor.yml
Normal file
9
appveyor.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: 1.0.{build}-{branch}
|
||||||
|
image: Visual Studio 2015
|
||||||
|
configuration: Release
|
||||||
|
platform: Any CPU
|
||||||
|
clone_depth: 10
|
||||||
|
build:
|
||||||
|
project: ArchiSteamFarm.sln
|
||||||
|
parallel: true
|
||||||
|
verbosity: minimal
|
||||||
Reference in New Issue
Block a user