Merge pull request #71 from Ryzhehvost/trade

Bot->Master trades
This commit is contained in:
Łukasz Domeradzki
2016-01-14 00:11:04 +01:00
7 changed files with 184 additions and 2 deletions

View File

@@ -21,7 +21,9 @@
limitations under the License.
*/
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;
using HtmlAgilityPack;
using SteamKit2;
using System;
@@ -371,6 +373,54 @@ namespace ArchiSteamFarm {
return response != null; // Steam API doesn't respond with any error code, assume any response is a success
}
internal async Task<List<SteamInventoryItem>> GetInventory() {
List<SteamInventoryItem> result = new List<SteamInventoryItem>();
try {
JObject jobj = await WebBrowser.UrlGetToJObject("http://steamcommunity.com/my/inventory/json/753/6", Cookie).ConfigureAwait(false);
IList<JToken> results = jobj.SelectTokens("$.rgInventory.*").ToList();
foreach (JToken res in results) {
result.Add(JsonConvert.DeserializeObject<SteamInventoryItem>(res.ToString()));
}
} catch (Exception) {
//just return empty list on error
}
return result;
}
internal async Task<bool> SendTradeOffer(List<SteamInventoryItem> itemsSend, string masterid,string token=null) {
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
}
SteamTradeItemList items = new SteamTradeItemList();
foreach (var item in itemsSend) {
items.assets.Add(new SteamTradeItem(753, 6, Int32.Parse(item.amount), item.id));
}
SteamTradeOfferRequest trade = new SteamTradeOfferRequest(true, 2, items, new SteamTradeItemList());
string referer = String.Format("https://steamcommunity.com/tradeoffer/new/?partner={0}", ((Int32)Int64.Parse(masterid)).ToString());
if (!string.IsNullOrEmpty(token)) {
referer += String.Format("&token={0}",token);
}
Dictionary <string, string> postData = new Dictionary<string, string>() {
{"sessionid", sessionID},
{"serverid","1" },
{"partner",masterid },
{"tradeoffermessage","sent by ASF" },
{"json_tradeoffer",JsonConvert.SerializeObject(trade) },
{"trade_offer_create_params",string.IsNullOrEmpty(token)?"":String.Format("{{ \"trade_offer_access_token\":\"{0}\" }}", token) }
};
HttpResponseMessage response = await WebBrowser.UrlPost("https://steamcommunity.com/tradeoffer/new/send", postData, Cookie, referer).ConfigureAwait(false);
if (response == null) {
return false;
}
return response.IsSuccessStatusCode;
}
internal async Task<HtmlDocument> GetBadgePage(int page) {
if (SteamID == 0 || page == 0) {
return null;

59
ArchiSteamFarm/Bot.cs Executable file → Normal file
View File

@@ -31,6 +31,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Text;
@@ -56,6 +57,7 @@ namespace ArchiSteamFarm {
internal readonly SteamFriends SteamFriends;
internal readonly SteamUser SteamUser;
internal readonly Trading Trading;
private Timer Timer;
private bool KeepRunning = true;
private bool InvalidPassword = false;
@@ -70,6 +72,7 @@ namespace ArchiSteamFarm {
internal string SteamPassword { get; private set; } = "null";
internal string SteamNickname { get; private set; } = "null";
internal string SteamApiKey { get; private set; } = "null";
internal string SteamTradeToken {get; private set; } = "null";
internal string SteamParentalPIN { get; private set; } = "0";
internal ulong SteamMasterID { get; private set; } = 0;
internal ulong SteamMasterClanID { get; private set; } = 0;
@@ -79,6 +82,8 @@ namespace ArchiSteamFarm {
internal bool ForwardKeysToOtherBots { get; private set; } = false;
internal bool UseAsfAsMobileAuthenticator { get; private set; } = false;
internal bool ShutdownOnFarmingFinished { get; private set; } = false;
internal bool SendOnFarmingFinished { get; private set; } = false;
internal uint SendTradePeriod {get; private set; } = 0;
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint>();
internal bool Statistics { get; private set; } = true;
@@ -305,6 +310,9 @@ namespace ArchiSteamFarm {
case "SteamApiKey":
SteamApiKey = value;
break;
case "SteamTradeToken":
SteamTradeToken = value;
break;
case "SteamParentalPIN":
SteamParentalPIN = value;
break;
@@ -332,6 +340,12 @@ namespace ArchiSteamFarm {
case "ShutdownOnFarmingFinished":
ShutdownOnFarmingFinished = bool.Parse(value);
break;
case "SendOnFarmingFinished":
SendOnFarmingFinished = bool.Parse(value);
break;
case "SendTradePeriod":
SendTradePeriod = uint.Parse(value);
break;
case "Blacklist":
Blacklist.Clear();
foreach (string appID in value.Split(',')) {
@@ -411,6 +425,9 @@ namespace ArchiSteamFarm {
}
internal async Task OnFarmingFinished() {
if (SendOnFarmingFinished) {
await ResponseSendTrade(BotName).ConfigureAwait(false);
}
if (ShutdownOnFarmingFinished) {
await Shutdown().ConfigureAwait(false);
}
@@ -467,6 +484,35 @@ namespace ArchiSteamFarm {
return result.ToString();
}
internal static async Task<string> ResponseSendTrade(string botName) {
Bot bot;
string token=null;
if (string.IsNullOrEmpty(botName)) {
return "Error, no name specified";
}
if (!Bots.TryGetValue(botName, out bot)) {
return "Couldn't find any bot named " + botName + "!";
}
if (bot.SendTradePeriod!=0) {
bot.Timer.Change(TimeSpan.FromHours(bot.SendTradePeriod),Timeout.InfiniteTimeSpan);
}
if (bot.SteamMasterID==0) {
return "No master set";
}
if ((!string.IsNullOrEmpty(bot.SteamTradeToken))&&(!bot.SteamTradeToken.Equals("null"))) {
token=bot.SteamTradeToken;
}
List<SteamInventoryItem> inv = await bot.ArchiWebHandler.GetInventory().ConfigureAwait(false);
if (inv.Count == 0) {
return "Nothing to send";
}
if (await bot.ArchiWebHandler.SendTradeOffer(inv, bot.SteamMasterID.ToString(),token).ConfigureAwait(false)) {
await bot.AcceptAllConfirmations().ConfigureAwait(false);
return "Trade offer sent";
}
return "Error sending trade offer";
}
internal static string Response2FA(string botName) {
if (string.IsNullOrEmpty(botName)) {
return null;
@@ -673,6 +719,8 @@ namespace ArchiSteamFarm {
return ResponseStatusAll();
case "!stop":
return await ResponseStop(BotName).ConfigureAwait(false);
case "!loot":
return await ResponseSendTrade(BotName).ConfigureAwait(false);
default:
return "Unrecognized command: " + message;
}
@@ -700,6 +748,8 @@ namespace ArchiSteamFarm {
return await ResponseStop(args[1]).ConfigureAwait(false);
case "!status":
return ResponseStatus(args[1]);
case "!loot":
return await ResponseSendTrade(args[1]).ConfigureAwait(false);
default:
return "Unrecognized command: " + args[0];
}
@@ -991,6 +1041,15 @@ namespace ArchiSteamFarm {
Trading.CheckTrades();
await CardsFarmer.StartFarming().ConfigureAwait(false);
if (SendTradePeriod!=0) {
Timer = new Timer(
async e => await ResponseSendTrade(BotName).ConfigureAwait(false),
null,
TimeSpan.FromHours(SendTradePeriod), // Delay
Timeout.InfiniteTimeSpan // Period
);
}
break;
case EResult.NoConnection:
case EResult.ServiceUnavailable:

View File

@@ -0,0 +1,12 @@

namespace ArchiSteamFarm
{
public class SteamInventoryItem
{
public string id { get; set; }
public string classid { get; set; }
public string instanceid { get; set; }
public string amount { get; set; }
public int pos { get; set; }
}
}

View File

@@ -0,0 +1,15 @@

namespace ArchiSteamFarm {
public class SteamTradeItem {
public int appid { get; set; }
public int contextid { get; set; }
public int amount { get; set; }
public string assetid { get; set; }
public SteamTradeItem (int aid, int cid, int am, string asset) {
appid = aid;
contextid = cid;
amount = am;
assetid = asset;
}
}
}

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace ArchiSteamFarm {
public class SteamTradeItemList {
public List<SteamTradeItem> assets { get; set; }
public List<string> currency { get; set; }
public bool ready { get; set; }
public SteamTradeItemList() {
assets = new List<SteamTradeItem>();
currency = new List<string>();
ready = false;
}
}
}

View File

@@ -0,0 +1,15 @@

namespace ArchiSteamFarm {
public class SteamTradeOfferRequest {
public bool newversion { get; set; }
public int version { get; set; }
public SteamTradeItemList me { get; set; }
public SteamTradeItemList them { get; set; }
public SteamTradeOfferRequest (bool nv, int v, SteamTradeItemList m, SteamTradeItemList t) {
newversion = nv;
version = v;
me = m;
them = t;
}
}
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- This is full-fledged example config, you may be also interested in minimal.xml for bare minimum one -->
<!-- This config includes all user-switchable properties that you may want to change on per-bot basis -->
<!-- Default values used in config match default ASF values when given config property is not found -->
<!-- In other words, if given property is not defined, ASF will assume default pre-programmed value -->
@@ -104,6 +104,23 @@
<!-- Personally I suggest leaving it at "false", unless you have a reason to close the process after all bots finished farming -->
<ShutdownOnFarmingFinished type="bool" value="false"/>
<!-- if this switch set to "true",the bot would try to send trade offer with all cards to master after farming is finished -->
<!-- for sucessfull trade offer bot needs SteamMasterID to be set properly -->
<!-- if the master is not a friend of this bot, SteamTradeToken would be needed also -->
<SendOnFarmingFinished type="bool" value="false"/>
<!-- If you bot has a lot of cards to farm, and you don't want to wait till it farm them all, you can use this parameter -->
<!-- this setting is the time period of auto-sending bot->master trades in hours-->
<!-- for example, if you want bot to send you trade offers once a day, set this to 24 -->
<!-- if value is "0" no automatic trade offers would be sent -->
<SendTradePeriod type="uint" value="0"/>
<!-- This is needed to send bot->master trades, only if master is not a friend of bot -->
<!-- To get this token go (as master!) here: http://steamcommunity.com/id/ryzhehvost/tradeoffers/privacy -->
<!-- the token is last part of link in the field "TradeURL", starting after "&token=" -->
<SteamTradeToken type="string" value="null"/>
<!-- This is comma-separated list of IDs that should not be considered for farming -->
<!-- Default value includes appIDs that are wrongly appearing on the profile, e.g. Summer Sale, Winter Sale or Monster Summer Game -->
<!-- In addition to blacklist defined here, ASF also has global blacklist, which is being updated on as-needed basis, so you don't need to update this entry -->