mirror of
https://github.com/JustArchiNET/ArchiSteamFarm
synced 2024-11-10 07:04:27 +00:00
Implement AngleSharp.XPath breaking changes
Bump of B warranted, more in the release notes
This commit is contained in:
parent
5c6ca3fee2
commit
f3229fa45f
10 changed files with 64 additions and 48 deletions
6
.github/renovate.json5
vendored
6
.github/renovate.json5
vendored
|
@ -19,12 +19,6 @@
|
|||
"allowedVersions": "<= 3.1",
|
||||
"matchManagers": [ "nuget" ],
|
||||
"matchPackageNames": [ "Microsoft.Extensions.Configuration.Json", "Microsoft.Extensions.Logging.Configuration" ]
|
||||
},
|
||||
{
|
||||
// TODO: https://github.com/AngleSharp/AngleSharp.XPath/issues/40
|
||||
"allowedVersions": "< 2.0",
|
||||
"matchManagers": [ "nuget" ],
|
||||
"matchPackageNames": [ "AngleSharp.XPath" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -189,24 +189,45 @@ public static class Utilities {
|
|||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static IEnumerable<IElement> SelectNodes(this IDocument document, string xpath) {
|
||||
public static IList<INode> SelectNodes(this IDocument document, string xpath) {
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
|
||||
return document.Body.SelectNodes(xpath).OfType<IElement>();
|
||||
return document.Body.SelectNodes(xpath);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static IElement? SelectSingleElementNode(this IElement element, string xpath) {
|
||||
public static IEnumerable<T> SelectNodes<T>(this IDocument document, string xpath) where T : class, INode {
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
|
||||
return document.SelectNodes(xpath).OfType<T>();
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static IEnumerable<T> SelectNodes<T>(this IElement element, string xpath) where T : class, INode {
|
||||
ArgumentNullException.ThrowIfNull(element);
|
||||
|
||||
return (IElement?) element.SelectSingleNode(xpath);
|
||||
return element.SelectNodes(xpath).OfType<T>();
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static IElement? SelectSingleNode(this IDocument document, string xpath) {
|
||||
public static INode? SelectSingleNode(this IDocument document, string xpath) {
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
|
||||
return (IElement?) document.Body.SelectSingleNode(xpath);
|
||||
return document.Body.SelectSingleNode(xpath);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static T? SelectSingleNode<T>(this IDocument document, string xpath) where T : class, INode {
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
|
||||
return document.Body.SelectSingleNode(xpath) as T;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static T? SelectSingleNode<T>(this IElement element, string xpath) where T : class, INode {
|
||||
ArgumentNullException.ThrowIfNull(element);
|
||||
|
||||
return element.SelectSingleNode(xpath) as T;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
|
|
|
@ -651,7 +651,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
|||
}
|
||||
|
||||
byte maxPages = 1;
|
||||
IElement? htmlNode = badgePage.SelectSingleNode("(//a[@class='pagelink'])[last()]");
|
||||
INode? htmlNode = badgePage.SelectSingleNode("(//a[@class='pagelink'])[last()]");
|
||||
|
||||
if (htmlNode != null) {
|
||||
string lastPage = htmlNode.TextContent;
|
||||
|
@ -1882,11 +1882,11 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
|||
|
||||
// We select badges that are ready to craft, as well as those that are already crafted to a maximum level, as those will not display with a craft button
|
||||
// Level 5 is maximum level for card badges according to https://steamcommunity.com/tradingcards/faq
|
||||
IEnumerable<IElement> linkElements = badgePage.SelectNodes("//a[@class='badge_craft_button'] | //div[@class='badges_sheet']/div[contains(@class, 'badge_row') and .//div[@class='badge_info_description']/div[contains(text(), 'Level 5')]]/a[@class='badge_row_overlay']");
|
||||
IEnumerable<IAttr> linkElements = badgePage.SelectNodes<IAttr>("//a[@class='badge_craft_button'] | //div[@class='badges_sheet']/div[contains(@class, 'badge_row') and .//div[@class='badge_info_description']/div[contains(text(), 'Level 5')]]/a[@class='badge_row_overlay']/@href");
|
||||
|
||||
HashSet<uint> result = new();
|
||||
|
||||
foreach (string? badgeUri in linkElements.Select(static htmlNode => htmlNode.GetAttribute("href"))) {
|
||||
foreach (string? badgeUri in linkElements.Select(static htmlNode => htmlNode.Value)) {
|
||||
if (string.IsNullOrEmpty(badgeUri)) {
|
||||
ArchiLogger.LogNullError(badgeUri);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ using System.Text.RegularExpressions;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.XPath;
|
||||
using ArchiSteamFarm.Collections;
|
||||
using ArchiSteamFarm.Core;
|
||||
using ArchiSteamFarm.Localization;
|
||||
|
@ -451,20 +452,20 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
|
|||
ArgumentNullException.ThrowIfNull(htmlDocument);
|
||||
ArgumentNullException.ThrowIfNull(parsedAppIDs);
|
||||
|
||||
IEnumerable<IElement> htmlNodes = htmlDocument.SelectNodes("//div[@class='badge_row_inner']");
|
||||
IEnumerable<IElement> htmlNodes = htmlDocument.SelectNodes<IElement>("//div[@class='badge_row_inner']");
|
||||
|
||||
HashSet<Task>? backgroundTasks = null;
|
||||
|
||||
foreach (IElement htmlNode in htmlNodes) {
|
||||
IElement? statsNode = htmlNode.SelectSingleElementNode(".//div[@class='badge_title_stats_content']");
|
||||
IElement? appIDNode = statsNode?.SelectSingleElementNode(".//div[@class='card_drop_info_dialog']");
|
||||
IElement? statsNode = htmlNode.SelectSingleNode<IElement>(".//div[@class='badge_title_stats_content']");
|
||||
IAttr? appIDNode = statsNode?.SelectSingleNode<IAttr>(".//div[@class='card_drop_info_dialog']/@id");
|
||||
|
||||
if (appIDNode == null) {
|
||||
// It's just a badge, nothing more
|
||||
continue;
|
||||
}
|
||||
|
||||
string? appIDText = appIDNode.GetAttribute("id");
|
||||
string appIDText = appIDNode.Value;
|
||||
|
||||
if (string.IsNullOrEmpty(appIDText)) {
|
||||
Bot.ArchiLogger.LogNullError(appIDText);
|
||||
|
@ -522,7 +523,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
|
|||
}
|
||||
|
||||
// Cards
|
||||
IElement? progressNode = statsNode?.SelectSingleElementNode(".//span[@class='progress_info_bold']");
|
||||
INode? progressNode = statsNode?.SelectSingleNode(".//span[@class='progress_info_bold']");
|
||||
|
||||
if (progressNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(progressNode);
|
||||
|
@ -561,7 +562,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
|
|||
}
|
||||
|
||||
// To save us on extra work, check cards earned so far first
|
||||
IElement? cardsEarnedNode = statsNode?.SelectSingleElementNode(".//div[@class='card_drop_info_header']");
|
||||
INode? cardsEarnedNode = statsNode?.SelectSingleNode(".//div[@class='card_drop_info_header']");
|
||||
|
||||
if (cardsEarnedNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(cardsEarnedNode);
|
||||
|
@ -606,7 +607,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
|
|||
}
|
||||
|
||||
// Hours
|
||||
IElement? timeNode = statsNode?.SelectSingleElementNode(".//div[@class='badge_title_stats_playtime']");
|
||||
INode? timeNode = statsNode?.SelectSingleNode(".//div[@class='badge_title_stats_playtime']");
|
||||
|
||||
if (timeNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(timeNode);
|
||||
|
@ -635,7 +636,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
|
|||
}
|
||||
|
||||
// Names
|
||||
IElement? nameNode = statsNode?.SelectSingleElementNode("(.//div[@class='card_drop_info_body'])[last()]");
|
||||
INode? nameNode = statsNode?.SelectSingleNode("(.//div[@class='card_drop_info_body'])[last()]");
|
||||
|
||||
if (nameNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameNode);
|
||||
|
@ -687,7 +688,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
|
|||
// Levels
|
||||
byte badgeLevel = 0;
|
||||
|
||||
IElement? levelNode = htmlNode.SelectSingleElementNode(".//div[@class='badge_info_description']/div[2]");
|
||||
INode? levelNode = htmlNode.SelectSingleNode(".//div[@class='badge_info_description']/div[2]");
|
||||
|
||||
if (levelNode != null) {
|
||||
// There is no levelNode if we didn't craft that badge yet (level 0)
|
||||
|
@ -1009,7 +1010,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
|
|||
|
||||
using IDocument? htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false);
|
||||
|
||||
IElement? progressNode = htmlDocument?.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||
INode? progressNode = htmlDocument?.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||
|
||||
if (progressNode == null) {
|
||||
return null;
|
||||
|
@ -1052,7 +1053,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable {
|
|||
|
||||
byte maxPages = 1;
|
||||
|
||||
IElement? htmlNode = htmlDocument.SelectSingleNode("(//a[@class='pagelink'])[last()]");
|
||||
INode? htmlNode = htmlDocument.SelectSingleNode("(//a[@class='pagelink'])[last()]");
|
||||
|
||||
if (htmlNode != null) {
|
||||
string lastPage = htmlNode.TextContent;
|
||||
|
|
|
@ -1695,16 +1695,16 @@ public sealed class ArchiWebHandler : IDisposable {
|
|||
return 0;
|
||||
}
|
||||
|
||||
IEnumerable<IElement> htmlNodes = htmlDocument.SelectNodes("//div[@class='badge_card_set_cards']/div[starts-with(@class, 'badge_card_set_card')]");
|
||||
IList<INode> htmlNodes = htmlDocument.SelectNodes("//div[@class='badge_card_set_cards']/div[starts-with(@class, 'badge_card_set_card')]");
|
||||
|
||||
result = (byte) htmlNodes.Count();
|
||||
|
||||
if (result == 0) {
|
||||
Bot.ArchiLogger.LogNullError(result);
|
||||
if (htmlNodes.Count == 0) {
|
||||
Bot.ArchiLogger.LogNullError(htmlNodes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = (byte) htmlNodes.Count;
|
||||
|
||||
ASF.GlobalDatabase?.CardCountsPerGame.TryAdd(appID, result);
|
||||
|
||||
return result;
|
||||
|
@ -1818,11 +1818,11 @@ public sealed class ArchiWebHandler : IDisposable {
|
|||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<IElement> htmlNodes = response.Content.SelectNodes("//div[@class='pending_gift']/div[starts-with(@id, 'pending_gift_')][count(div[@class='pending_giftcard_leftcol']) > 0]/@id");
|
||||
IEnumerable<IAttr> htmlNodes = response.Content.SelectNodes<IAttr>("//div[@class='pending_gift']/div[starts-with(@id, 'pending_gift_')][count(div[@class='pending_giftcard_leftcol']) > 0]/@id");
|
||||
|
||||
HashSet<ulong> results = new();
|
||||
|
||||
foreach (string? giftCardIDText in htmlNodes.Select(static node => node.GetAttribute("id"))) {
|
||||
foreach (string? giftCardIDText in htmlNodes.Select(static node => node.Value)) {
|
||||
if (string.IsNullOrEmpty(giftCardIDText)) {
|
||||
Bot.ArchiLogger.LogNullError(giftCardIDText);
|
||||
|
||||
|
@ -1865,11 +1865,11 @@ public sealed class ArchiWebHandler : IDisposable {
|
|||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<IElement> htmlNodes = response.Content.SelectNodes("(//table[@class='accountTable'])[2]//a/@data-miniprofile");
|
||||
IEnumerable<IAttr> htmlNodes = response.Content.SelectNodes<IAttr>("(//table[@class='accountTable'])[2]//a/@data-miniprofile");
|
||||
|
||||
HashSet<ulong> result = new();
|
||||
|
||||
foreach (string? miniProfile in htmlNodes.Select(static htmlNode => htmlNode.GetAttribute("data-miniprofile"))) {
|
||||
foreach (string? miniProfile in htmlNodes.Select(static htmlNode => htmlNode.Value)) {
|
||||
if (string.IsNullOrEmpty(miniProfile)) {
|
||||
Bot.ArchiLogger.LogNullError(miniProfile);
|
||||
|
||||
|
@ -1953,7 +1953,7 @@ public sealed class ArchiWebHandler : IDisposable {
|
|||
|
||||
using HtmlDocumentResponse? response = await UrlGetToHtmlDocumentWithSession(request, checkSessionPreemptively: false).ConfigureAwait(false);
|
||||
|
||||
IElement? htmlNode = response?.Content?.SelectSingleNode("//div[@class='pagecontent']/script");
|
||||
INode? htmlNode = response?.Content?.SelectSingleNode("//div[@class='pagecontent']/script");
|
||||
|
||||
if (htmlNode == null) {
|
||||
// Trade can be no longer valid
|
||||
|
@ -2345,7 +2345,7 @@ public sealed class ArchiWebHandler : IDisposable {
|
|||
return (ESteamApiKeyState.Timeout, null);
|
||||
}
|
||||
|
||||
IElement? titleNode = response.Content.SelectSingleNode("//div[@id='mainContents']/h2");
|
||||
INode? titleNode = response.Content.SelectSingleNode("//div[@id='mainContents']/h2");
|
||||
|
||||
if (titleNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(titleNode);
|
||||
|
@ -2365,7 +2365,7 @@ public sealed class ArchiWebHandler : IDisposable {
|
|||
return (ESteamApiKeyState.AccessDenied, null);
|
||||
}
|
||||
|
||||
IElement? htmlNode = response.Content.SelectSingleNode("//div[@id='bodyContents_ex']/p");
|
||||
INode? htmlNode = response.Content.SelectSingleNode("//div[@id='bodyContents_ex']/p");
|
||||
|
||||
if (htmlNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(htmlNode);
|
||||
|
|
|
@ -93,7 +93,7 @@ internal sealed class SteamSaleEvent : IAsyncDisposable, IDisposable {
|
|||
return null;
|
||||
}
|
||||
|
||||
IElement? htmlNode = htmlDocument.SelectSingleNode("//div[@class='subtext']");
|
||||
INode? htmlNode = htmlDocument.SelectSingleNode("//div[@class='subtext']");
|
||||
|
||||
if (htmlNode == null) {
|
||||
// Valid, no cards for exploring the queue available
|
||||
|
|
|
@ -121,7 +121,7 @@ public sealed class MobileAuthenticator : IDisposable {
|
|||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<IElement> confirmationNodes = htmlDocument.SelectNodes("//div[@class='mobileconf_list_entry']");
|
||||
IEnumerable<IElement> confirmationNodes = htmlDocument.SelectNodes<IElement>("//div[@class='mobileconf_list_entry']");
|
||||
|
||||
HashSet<Confirmation> result = new();
|
||||
|
||||
|
|
|
@ -86,12 +86,12 @@ internal static class GitHub {
|
|||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<IElement> revisionNodes = response.Content.SelectNodes("//li[contains(@class, 'wiki-history-revision')]");
|
||||
IEnumerable<IElement> revisionNodes = response.Content.SelectNodes<IElement>("//li[contains(@class, 'wiki-history-revision')]");
|
||||
|
||||
Dictionary<string, DateTime> result = new();
|
||||
|
||||
foreach (IElement revisionNode in revisionNodes) {
|
||||
IElement? versionNode = revisionNode.SelectSingleElementNode(".//input/@value");
|
||||
IAttr? versionNode = revisionNode.SelectSingleNode<IAttr>(".//input/@value");
|
||||
|
||||
if (versionNode == null) {
|
||||
ASF.ArchiLogger.LogNullError(versionNode);
|
||||
|
@ -99,7 +99,7 @@ internal static class GitHub {
|
|||
return null;
|
||||
}
|
||||
|
||||
string? versionText = versionNode.GetAttribute("value");
|
||||
string versionText = versionNode.Value;
|
||||
|
||||
if (string.IsNullOrEmpty(versionText)) {
|
||||
ASF.ArchiLogger.LogNullError(versionText);
|
||||
|
@ -107,7 +107,7 @@ internal static class GitHub {
|
|||
return null;
|
||||
}
|
||||
|
||||
IElement? dateTimeNode = revisionNode.SelectSingleElementNode(".//relative-time/@datetime");
|
||||
IAttr? dateTimeNode = revisionNode.SelectSingleNode<IAttr>(".//relative-time/@datetime");
|
||||
|
||||
if (dateTimeNode == null) {
|
||||
ASF.ArchiLogger.LogNullError(dateTimeNode);
|
||||
|
@ -115,7 +115,7 @@ internal static class GitHub {
|
|||
return null;
|
||||
}
|
||||
|
||||
string? dateTimeText = dateTimeNode.GetAttribute("datetime");
|
||||
string dateTimeText = dateTimeNode.Value;
|
||||
|
||||
if (string.IsNullOrEmpty(dateTimeText)) {
|
||||
ASF.ArchiLogger.LogNullError(dateTimeText);
|
||||
|
@ -153,7 +153,7 @@ internal static class GitHub {
|
|||
return null;
|
||||
}
|
||||
|
||||
IElement? markdownBodyNode = response.Content.SelectSingleNode("//div[@class='markdown-body']");
|
||||
IElement? markdownBodyNode = response.Content.SelectSingleNode<IElement>("//div[@class='markdown-body']");
|
||||
|
||||
return markdownBodyNode?.InnerHtml.Trim() ?? "";
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>5.2.9.0</Version>
|
||||
<Version>5.3.0.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="AngleSharp.XPath" Version="1.1.7" />
|
||||
<PackageVersion Include="AngleSharp.XPath" Version="2.0.1" />
|
||||
<PackageVersion Include="ConfigureAwaitChecker.Analyzer" Version="5.0.0.1" />
|
||||
<PackageVersion Include="CryptSharpStandard" Version="1.0.0" />
|
||||
<PackageVersion Include="Humanizer" Version="2.14.1" />
|
||||
|
|
Loading…
Reference in a new issue