mirror of
https://github.com/JustArchiNET/ArchiSteamFarm
synced 2024-11-10 15:14:41 +00:00
Rewrite #414 in a way that makes me happy
This commit is contained in:
parent
d188065490
commit
de897aaa92
7 changed files with 252 additions and 199 deletions
|
@ -64,19 +64,9 @@ namespace ArchiSteamFarm {
|
||||||
internal bool Ready { get; private set; }
|
internal bool Ready { get; private set; }
|
||||||
|
|
||||||
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
|
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
|
||||||
|
private string SteamApiKey;
|
||||||
private ulong SteamID;
|
private ulong SteamID;
|
||||||
|
|
||||||
private string _SteamApiKey;
|
|
||||||
internal string SteamApiKey => ObtainApiKey().Result;
|
|
||||||
private enum ESteamApiKeyState : byte {
|
|
||||||
Unknown = 0,
|
|
||||||
Invalid = 1,
|
|
||||||
Registered = 2,
|
|
||||||
NotRegistered = 3,
|
|
||||||
AccessDenied = 4,
|
|
||||||
}
|
|
||||||
private ESteamApiKeyState SteamApiKeyState;
|
|
||||||
|
|
||||||
internal ArchiWebHandler(Bot bot) {
|
internal ArchiWebHandler(Bot bot) {
|
||||||
if (bot == null) {
|
if (bot == null) {
|
||||||
throw new ArgumentNullException(nameof(bot));
|
throw new ArgumentNullException(nameof(bot));
|
||||||
|
@ -95,7 +85,7 @@ namespace ArchiSteamFarm {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +113,7 @@ namespace ArchiSteamFarm {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +141,7 @@ namespace ArchiSteamFarm {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,15 +161,20 @@ namespace ArchiSteamFarm {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
internal void DeclineTradeOffer(ulong tradeID) {
|
internal async Task DeclineTradeOffer(ulong tradeID) {
|
||||||
if ((tradeID == 0) || string.IsNullOrEmpty(SteamApiKey)) {
|
if (tradeID == 0) {
|
||||||
Bot.ArchiLogger.LogNullError(nameof(tradeID) + " || " + nameof(SteamApiKey));
|
Bot.ArchiLogger.LogNullError(nameof(tradeID));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string steamApiKey = await GetApiKey().ConfigureAwait(false);
|
||||||
|
if (string.IsNullOrEmpty(SteamApiKey)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyValue response = null;
|
KeyValue response = null;
|
||||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||||
using (dynamic iEconService = WebAPI.GetInterface(IEconService, SteamApiKey)) {
|
using (dynamic iEconService = WebAPI.GetInterface(IEconService, steamApiKey)) {
|
||||||
iEconService.Timeout = Timeout;
|
iEconService.Timeout = Timeout;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -201,7 +196,7 @@ namespace ArchiSteamFarm {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
internal async Task<HashSet<uint>> GenerateNewDiscoveryQueue() {
|
internal async Task<HashSet<uint>> GenerateNewDiscoveryQueue() {
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,15 +217,15 @@ namespace ArchiSteamFarm {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
internal HashSet<Steam.TradeOffer> GetActiveTradeOffers() {
|
internal async Task<HashSet<Steam.TradeOffer>> GetActiveTradeOffers() {
|
||||||
|
string steamApiKey = await GetApiKey().ConfigureAwait(false);
|
||||||
if (string.IsNullOrEmpty(SteamApiKey)) {
|
if (string.IsNullOrEmpty(SteamApiKey)) {
|
||||||
Bot.ArchiLogger.LogNullError(nameof(SteamApiKey));
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyValue response = null;
|
KeyValue response = null;
|
||||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||||
using (dynamic iEconService = WebAPI.GetInterface(IEconService, SteamApiKey)) {
|
using (dynamic iEconService = WebAPI.GetInterface(IEconService, steamApiKey)) {
|
||||||
iEconService.Timeout = Timeout;
|
iEconService.Timeout = Timeout;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -332,114 +327,13 @@ namespace ArchiSteamFarm {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ESteamApiKeyState> UpdateApiKey() {
|
|
||||||
if ( !await RefreshSessionIfNeeded().ConfigureAwait(false) ) {
|
|
||||||
return ESteamApiKeyState.Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
string request = SteamCommunityURL + "/dev/apikey?l=english";
|
|
||||||
|
|
||||||
HtmlDocument htmlDocument = await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
|
||||||
|
|
||||||
HtmlNode titleNode = htmlDocument?.DocumentNode.SelectSingleNode("//div[@id='mainContents']/h2");
|
|
||||||
|
|
||||||
if (titleNode == null) {
|
|
||||||
return ESteamApiKeyState.Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
string title = titleNode.InnerText;
|
|
||||||
|
|
||||||
if (title.IndexOf("Access Denied") == 0) {
|
|
||||||
return ESteamApiKeyState.AccessDenied;
|
|
||||||
}
|
|
||||||
|
|
||||||
HtmlNode htmlNode = htmlDocument?.DocumentNode.SelectSingleNode("//div[@id='bodyContents_ex']/p");
|
|
||||||
if (htmlNode == null) {
|
|
||||||
return ESteamApiKeyState.Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
string text = htmlNode.InnerText;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(text)) {
|
|
||||||
Bot.ArchiLogger.LogNullError(nameof(text));
|
|
||||||
return ESteamApiKeyState.Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hintIndex = text.IndexOf("Registering for a Steam Web API Key", StringComparison.Ordinal);
|
|
||||||
if (hintIndex == 0) {
|
|
||||||
return ESteamApiKeyState.NotRegistered;
|
|
||||||
}
|
|
||||||
|
|
||||||
int keyIndex = text.IndexOf("Key: ", StringComparison.Ordinal);
|
|
||||||
if ( keyIndex < 0 ) {
|
|
||||||
Bot.ArchiLogger.LogNullError(nameof(keyIndex));
|
|
||||||
return ESteamApiKeyState.Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyIndex += 5;
|
|
||||||
text = text.Substring(keyIndex);
|
|
||||||
|
|
||||||
if ( text.Length != 32 ) {
|
|
||||||
Bot.ArchiLogger.LogNullError(nameof(text));
|
|
||||||
return ESteamApiKeyState.Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
string allowedChars = "0123456789ABCDEF";
|
|
||||||
foreach (char c in text) {
|
|
||||||
if (!allowedChars.Contains(c.ToString()) ) {
|
|
||||||
Bot.ArchiLogger.LogNullError(nameof(text));
|
|
||||||
return ESteamApiKeyState.Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._SteamApiKey = text;
|
|
||||||
|
|
||||||
return ESteamApiKeyState.Registered;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> RegisterApiKey() {
|
|
||||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
|
||||||
if ( string.IsNullOrEmpty(sessionID) ) {
|
|
||||||
Bot.ArchiLogger.LogNullError(nameof(sessionID));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string request = SteamCommunityURL + "/dev/registerkey";
|
|
||||||
Dictionary<string, string> data = new Dictionary<string, string>(4) {
|
|
||||||
{"domain", "localhost" },
|
|
||||||
{"agreeToTerms", "agreed"},
|
|
||||||
{"sessionid", sessionID},
|
|
||||||
{"Submit", "Register"}
|
|
||||||
};
|
|
||||||
|
|
||||||
return await WebBrowser.UrlPostRetry( request, data).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> ObtainApiKey() {
|
|
||||||
switch (SteamApiKeyState) {
|
|
||||||
case ESteamApiKeyState.Unknown:
|
|
||||||
SteamApiKeyState = await UpdateApiKey().ConfigureAwait(false);
|
|
||||||
return await ObtainApiKey().ConfigureAwait(false);
|
|
||||||
case ESteamApiKeyState.NotRegistered:
|
|
||||||
await RegisterApiKey().ConfigureAwait(false);
|
|
||||||
SteamApiKeyState = ESteamApiKeyState.Unknown;
|
|
||||||
return await ObtainApiKey().ConfigureAwait(false);
|
|
||||||
case ESteamApiKeyState.Registered:
|
|
||||||
return _SteamApiKey;
|
|
||||||
case ESteamApiKeyState.AccessDenied:
|
|
||||||
return string.Empty;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<HtmlDocument> GetBadgePage(byte page) {
|
internal async Task<HtmlDocument> GetBadgePage(byte page) {
|
||||||
if (page == 0) {
|
if (page == 0) {
|
||||||
Bot.ArchiLogger.LogNullError(nameof(page));
|
Bot.ArchiLogger.LogNullError(nameof(page));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +347,7 @@ namespace ArchiSteamFarm {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,7 +368,7 @@ namespace ArchiSteamFarm {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +378,7 @@ namespace ArchiSteamFarm {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
internal async Task<HtmlDocument> GetDiscoveryQueuePage() {
|
internal async Task<HtmlDocument> GetDiscoveryQueuePage() {
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,7 +388,7 @@ namespace ArchiSteamFarm {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
internal async Task<HashSet<ulong>> GetFamilySharingSteamIDs() {
|
internal async Task<HashSet<ulong>> GetFamilySharingSteamIDs() {
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,7 +427,7 @@ namespace ArchiSteamFarm {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,13 +435,53 @@ namespace ArchiSteamFarm {
|
||||||
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task<Dictionary<uint, string>> GetMyOwnedGames() {
|
||||||
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string request = SteamCommunityURL + "/my/games/?xml=1";
|
||||||
|
|
||||||
|
XmlDocument response = await WebBrowser.UrlGetToXMLRetry(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
XmlNodeList xmlNodeList = response?.SelectNodes("gamesList/games/game");
|
||||||
|
if ((xmlNodeList == null) || (xmlNodeList.Count == 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<uint, string> result = new Dictionary<uint, string>(xmlNodeList.Count);
|
||||||
|
foreach (XmlNode xmlNode in xmlNodeList) {
|
||||||
|
XmlNode appNode = xmlNode.SelectSingleNode("appID");
|
||||||
|
if (appNode == null) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(appNode));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint appID;
|
||||||
|
if (!uint.TryParse(appNode.InnerText, out appID)) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(appID));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlNode nameNode = xmlNode.SelectSingleNode("name");
|
||||||
|
if (nameNode == null) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(nameNode));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[appID] = nameNode.InnerText;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task<HashSet<Steam.Item>> GetMySteamInventory(bool tradable, HashSet<Steam.Item.EType> wantedTypes) {
|
internal async Task<HashSet<Steam.Item>> GetMySteamInventory(bool tradable, HashSet<Steam.Item.EType> wantedTypes) {
|
||||||
if ((wantedTypes == null) || (wantedTypes.Count == 0)) {
|
if ((wantedTypes == null) || (wantedTypes.Count == 0)) {
|
||||||
Bot.ArchiLogger.LogNullError(nameof(wantedTypes));
|
Bot.ArchiLogger.LogNullError(nameof(wantedTypes));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -666,55 +600,20 @@ namespace ArchiSteamFarm {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
|
internal async Task<Dictionary<uint, string>> GetOwnedGames(ulong steamID) {
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (steamID == 0) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(steamID));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string request = SteamCommunityURL + "/my/games/?xml=1";
|
string steamApiKey = await GetApiKey().ConfigureAwait(false);
|
||||||
|
if (string.IsNullOrEmpty(SteamApiKey)) {
|
||||||
XmlDocument response = await WebBrowser.UrlGetToXMLRetry(request).ConfigureAwait(false);
|
|
||||||
|
|
||||||
XmlNodeList xmlNodeList = response?.SelectNodes("gamesList/games/game");
|
|
||||||
if ((xmlNodeList == null) || (xmlNodeList.Count == 0)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<uint, string> result = new Dictionary<uint, string>(xmlNodeList.Count);
|
|
||||||
foreach (XmlNode xmlNode in xmlNodeList) {
|
|
||||||
XmlNode appNode = xmlNode.SelectSingleNode("appID");
|
|
||||||
if (appNode == null) {
|
|
||||||
Bot.ArchiLogger.LogNullError(nameof(appNode));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint appID;
|
|
||||||
if (!uint.TryParse(appNode.InnerText, out appID)) {
|
|
||||||
Bot.ArchiLogger.LogNullError(nameof(appID));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
XmlNode nameNode = xmlNode.SelectSingleNode("name");
|
|
||||||
if (nameNode == null) {
|
|
||||||
Bot.ArchiLogger.LogNullError(nameof(nameNode));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
result[appID] = nameNode.InnerText;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Dictionary<uint, string> GetOwnedGames(ulong steamID) {
|
|
||||||
if ((steamID == 0) || string.IsNullOrEmpty(SteamApiKey)) {
|
|
||||||
Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(SteamApiKey));
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyValue response = null;
|
KeyValue response = null;
|
||||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||||
using (dynamic iPlayerService = WebAPI.GetInterface(IPlayerService, SteamApiKey)) {
|
using (dynamic iPlayerService = WebAPI.GetInterface(IPlayerService, steamApiKey)) {
|
||||||
iPlayerService.Timeout = Timeout;
|
iPlayerService.Timeout = Timeout;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -775,7 +674,7 @@ namespace ArchiSteamFarm {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
internal async Task<HtmlDocument> GetSteamAwardsPage() {
|
internal async Task<HtmlDocument> GetSteamAwardsPage() {
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,7 +689,7 @@ namespace ArchiSteamFarm {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,7 +740,7 @@ namespace ArchiSteamFarm {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -857,7 +756,7 @@ namespace ArchiSteamFarm {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -881,6 +780,8 @@ namespace ArchiSteamFarm {
|
||||||
return response?.Success;
|
return response?.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task<bool> HasValidApiKey() => !string.IsNullOrEmpty(await GetApiKey().ConfigureAwait(false));
|
||||||
|
|
||||||
internal static void Init() => Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
internal static void Init() => Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
||||||
|
|
||||||
internal async Task<bool> Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalPin) {
|
internal async Task<bool> Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalPin) {
|
||||||
|
@ -976,7 +877,7 @@ namespace ArchiSteamFarm {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -996,7 +897,7 @@ namespace ArchiSteamFarm {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> MarkInventory() {
|
internal async Task<bool> MarkInventory() {
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,7 +913,7 @@ namespace ArchiSteamFarm {
|
||||||
return ArchiHandler.PurchaseResponseCallback.EPurchaseResult.Unknown;
|
return ArchiHandler.PurchaseResponseCallback.EPurchaseResult.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return ArchiHandler.PurchaseResponseCallback.EPurchaseResult.Timeout;
|
return ArchiHandler.PurchaseResponseCallback.EPurchaseResult.Timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1031,7 +932,7 @@ namespace ArchiSteamFarm {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1078,6 +979,122 @@ namespace ArchiSteamFarm {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> GetApiKey(bool allowRegister = true) {
|
||||||
|
switch (SteamApiKey) {
|
||||||
|
case null:
|
||||||
|
// We didn't fetch API key yet
|
||||||
|
Tuple<ESteamApiKeyState, string> result = await GetApiKeyState().ConfigureAwait(false);
|
||||||
|
if (result == null) {
|
||||||
|
// Request timed out, bad luck, we'll try again later
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (result.Item1) {
|
||||||
|
case ESteamApiKeyState.Registered:
|
||||||
|
// We succeeded in fetching API key, and it resulted in registered key
|
||||||
|
// Cache the result and return it
|
||||||
|
SteamApiKey = result.Item2;
|
||||||
|
return SteamApiKey;
|
||||||
|
case ESteamApiKeyState.NotRegisteredYet:
|
||||||
|
// We succeeded in fetching API key, and it resulted in no key registered yet
|
||||||
|
if (!allowRegister) {
|
||||||
|
// But this call doesn't allow us to register it, so return null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're allowed to register the key, let's do so
|
||||||
|
if (!await RegisterApiKey().ConfigureAwait(false)) {
|
||||||
|
// Request timed out, bad luck, we'll try again later
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should have key ready, to let's call GetApiKey() recursively, but don't allow further recursion
|
||||||
|
return await GetApiKey(false).ConfigureAwait(false);
|
||||||
|
case ESteamApiKeyState.AccessDenied:
|
||||||
|
// We succeeded in fetching API key, but it resulted in access denied
|
||||||
|
// Cache the result as empty, and return null
|
||||||
|
SteamApiKey = "";
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
// We got some kind of error, maybe it's temporary, maybe it's permanent
|
||||||
|
// Don't cache anything, we'll try again later
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case "":
|
||||||
|
// API key is permanently unavailable via AccessDenied
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
// We have key already fetched and cached, return it
|
||||||
|
return SteamApiKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Tuple<ESteamApiKeyState, string>> GetApiKeyState() {
|
||||||
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string request = SteamCommunityURL + "/dev/apikey?l=english";
|
||||||
|
HtmlDocument htmlDocument = await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
HtmlNode titleNode = htmlDocument?.DocumentNode.SelectSingleNode("//div[@id='mainContents']/h2");
|
||||||
|
if (titleNode == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string title = titleNode.InnerText;
|
||||||
|
if (string.IsNullOrEmpty(title)) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(title));
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.Error, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (title.Contains("Access Denied")) {
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.AccessDenied, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@id='bodyContents_ex']/p");
|
||||||
|
if (htmlNode == null) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(htmlNode));
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.Error, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
string text = htmlNode.InnerText;
|
||||||
|
if (string.IsNullOrEmpty(text)) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(text));
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.Error, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.Contains("Registering for a Steam Web API Key")) {
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.NotRegisteredYet, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
int keyIndex = text.IndexOf("Key: ", StringComparison.Ordinal);
|
||||||
|
if (keyIndex < 0) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(keyIndex));
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.Error, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyIndex += 5;
|
||||||
|
|
||||||
|
if (text.Length <= keyIndex) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(text));
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.Error, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.Substring(keyIndex);
|
||||||
|
if (text.Length != 32) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(text));
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.Error, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utilities.IsValidHexadecimalString(text)) {
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.Registered, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(text));
|
||||||
|
return new Tuple<ESteamApiKeyState, string>(ESteamApiKeyState.Error, null);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
internal async Task<bool> SteamAwardsVote(byte voteID, uint appID) {
|
internal async Task<bool> SteamAwardsVote(byte voteID, uint appID) {
|
||||||
if ((voteID == 0) || (appID == 0)) {
|
if ((voteID == 0) || (appID == 0)) {
|
||||||
|
@ -1085,7 +1102,7 @@ namespace ArchiSteamFarm {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1230,6 +1247,28 @@ namespace ArchiSteamFarm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<bool> RegisterApiKey() {
|
||||||
|
if (!Ready || !await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||||
|
if (string.IsNullOrEmpty(sessionID)) {
|
||||||
|
Bot.ArchiLogger.LogNullError(nameof(sessionID));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string request = SteamCommunityURL + "/dev/registerkey";
|
||||||
|
Dictionary<string, string> data = new Dictionary<string, string>(4) {
|
||||||
|
{ "domain", "localhost" },
|
||||||
|
{ "agreeToTerms", "agreed" },
|
||||||
|
{ "sessionid", sessionID },
|
||||||
|
{ "Submit", "Register" }
|
||||||
|
};
|
||||||
|
|
||||||
|
return await WebBrowser.UrlPostRetry(request, data).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<bool> UnlockParentalAccount(string parentalPin) {
|
private async Task<bool> UnlockParentalAccount(string parentalPin) {
|
||||||
if (string.IsNullOrEmpty(parentalPin)) {
|
if (string.IsNullOrEmpty(parentalPin)) {
|
||||||
Bot.ArchiLogger.LogNullError(nameof(parentalPin));
|
Bot.ArchiLogger.LogNullError(nameof(parentalPin));
|
||||||
|
@ -1252,5 +1291,12 @@ namespace ArchiSteamFarm {
|
||||||
Bot.ArchiLogger.LogGenericInfo(Strings.Success);
|
Bot.ArchiLogger.LogGenericInfo(Strings.Success);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum ESteamApiKeyState : byte {
|
||||||
|
Error,
|
||||||
|
Registered,
|
||||||
|
NotRegisteredYet,
|
||||||
|
AccessDenied
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -60,7 +60,6 @@ namespace ArchiSteamFarm {
|
||||||
internal readonly string BotName;
|
internal readonly string BotName;
|
||||||
|
|
||||||
internal bool HasMobileAuthenticator => BotDatabase?.MobileAuthenticator != null;
|
internal bool HasMobileAuthenticator => BotDatabase?.MobileAuthenticator != null;
|
||||||
internal bool HasValidApiKey => !string.IsNullOrEmpty(ArchiWebHandler.SteamApiKey);
|
|
||||||
internal bool IsConnectedAndLoggedOn => (SteamClient?.IsConnected == true) && (SteamClient.SteamID != null);
|
internal bool IsConnectedAndLoggedOn => (SteamClient?.IsConnected == true) && (SteamClient.SteamID != null);
|
||||||
internal bool IsPlayingPossible => !PlayingBlocked && (LibraryLockedBySteamID == 0);
|
internal bool IsPlayingPossible => !PlayingBlocked && (LibraryLockedBySteamID == 0);
|
||||||
|
|
||||||
|
@ -1832,10 +1831,10 @@ namespace ArchiSteamFarm {
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary<uint, string> ownedGames;
|
Dictionary<uint, string> ownedGames;
|
||||||
if (HasValidApiKey) {
|
if (await ArchiWebHandler.HasValidApiKey().ConfigureAwait(false)) {
|
||||||
ownedGames = ArchiWebHandler.GetOwnedGames(SteamClient.SteamID);
|
ownedGames = await ArchiWebHandler.GetOwnedGames(SteamClient.SteamID).ConfigureAwait(false);
|
||||||
} else {
|
} else {
|
||||||
ownedGames = await ArchiWebHandler.GetOwnedGames().ConfigureAwait(false);
|
ownedGames = await ArchiWebHandler.GetMyOwnedGames().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ownedGames == null) || (ownedGames.Count == 0)) {
|
if ((ownedGames == null) || (ownedGames.Count == 0)) {
|
||||||
|
|
|
@ -40,9 +40,6 @@ namespace ArchiSteamFarm {
|
||||||
private readonly Bot Bot;
|
private readonly Bot Bot;
|
||||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
||||||
|
|
||||||
private bool HasAutomatedTrading => Bot.HasMobileAuthenticator && Bot.HasValidApiKey;
|
|
||||||
private bool SteamTradeMatcher => Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher);
|
|
||||||
|
|
||||||
private string LastAvatarHash;
|
private string LastAvatarHash;
|
||||||
private DateTime LastHeartBeat = DateTime.MinValue;
|
private DateTime LastHeartBeat = DateTime.MinValue;
|
||||||
private bool? LastMatchEverything;
|
private bool? LastMatchEverything;
|
||||||
|
@ -95,7 +92,7 @@ namespace ArchiSteamFarm {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't announce if we don't meet conditions
|
// Don't announce if we don't meet conditions
|
||||||
if (!HasAutomatedTrading || !SteamTradeMatcher) {
|
if (!Bot.HasMobileAuthenticator || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher) || !await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false)) {
|
||||||
ShouldSendHeartBeats = false;
|
ShouldSendHeartBeats = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,11 +91,7 @@ namespace ArchiSteamFarm {
|
||||||
internal void OnDisconnected() => IgnoredTrades.ClearAndTrim();
|
internal void OnDisconnected() => IgnoredTrades.ClearAndTrim();
|
||||||
|
|
||||||
private async Task ParseActiveTrades() {
|
private async Task ParseActiveTrades() {
|
||||||
if (!Bot.HasValidApiKey) {
|
HashSet<Steam.TradeOffer> tradeOffers = await Bot.ArchiWebHandler.GetActiveTradeOffers().ConfigureAwait(false);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashSet<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetActiveTradeOffers();
|
|
||||||
if ((tradeOffers == null) || (tradeOffers.Count == 0)) {
|
if ((tradeOffers == null) || (tradeOffers.Count == 0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -150,7 +146,7 @@ namespace ArchiSteamFarm {
|
||||||
if (result.Result == ParseTradeResult.EResult.RejectedPermanently) {
|
if (result.Result == ParseTradeResult.EResult.RejectedPermanently) {
|
||||||
if (Bot.BotConfig.IsBotAccount) {
|
if (Bot.BotConfig.IsBotAccount) {
|
||||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.RejectingTrade, tradeOffer.TradeOfferID));
|
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.RejectingTrade, tradeOffer.TradeOfferID));
|
||||||
Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID);
|
await Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
@ -76,6 +77,25 @@ namespace ArchiSteamFarm {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
internal static bool IsValidHexadecimalString(string text) {
|
||||||
|
if (string.IsNullOrEmpty(text)) {
|
||||||
|
Program.ArchiLogger.LogNullError(nameof(text));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte split = 16;
|
||||||
|
for (byte i = 0; i < text.Length; i += split) {
|
||||||
|
string textPart = string.Join("", text.Skip(i).Take(split));
|
||||||
|
|
||||||
|
ulong ignored;
|
||||||
|
if (!ulong.TryParse(textPart, NumberStyles.HexNumber, null, out ignored)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
internal static string ToHumanReadable(this TimeSpan timeSpan) {
|
internal static string ToHumanReadable(this TimeSpan timeSpan) {
|
||||||
// It's really dirty, I'd appreciate a lot if C# offered nice TimeSpan formatting by default
|
// It's really dirty, I'd appreciate a lot if C# offered nice TimeSpan formatting by default
|
||||||
// Normally I'd use third-party library like Humanizer, but using it only for this bit is not worth it
|
// Normally I'd use third-party library like Humanizer, but using it only for this bit is not worth it
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
"SendOnFarmingFinished": false,
|
"SendOnFarmingFinished": false,
|
||||||
"SendTradePeriod": 0,
|
"SendTradePeriod": 0,
|
||||||
"ShutdownOnFarmingFinished": false,
|
"ShutdownOnFarmingFinished": false,
|
||||||
"SteamApiKey": null,
|
|
||||||
"SteamLogin": null,
|
"SteamLogin": null,
|
||||||
"SteamMasterClanID": 0,
|
"SteamMasterClanID": 0,
|
||||||
"SteamMasterID": 0,
|
"SteamMasterID": 0,
|
||||||
|
|
|
@ -108,10 +108,6 @@ namespace ConfigGenerator {
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool ShutdownOnFarmingFinished { get; set; } = false;
|
public bool ShutdownOnFarmingFinished { get; set; } = false;
|
||||||
|
|
||||||
[LocalizedCategory("Access")]
|
|
||||||
[JsonProperty]
|
|
||||||
public string SteamApiKey { get; set; } = null;
|
|
||||||
|
|
||||||
[LocalizedCategory("Core")]
|
[LocalizedCategory("Core")]
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
public string SteamLogin { get; set; } = null;
|
public string SteamLogin { get; set; } = null;
|
||||||
|
|
Loading…
Reference in a new issue