Not getting correct data from span - c#

I've been making a custom user handler for Jessecar's SteamBot, which is unrelated to the problem I'm having, but essentially what I've done, is I've made it so you can set the bot to play a specific game by App ID, and I've been using this to idle on games for Steam Trading Cards, the only issue is, the only way I can check if it's finished, is by checking its inventory and how many cards are supposed to drop, which isn't too much of a hassle, but the main reason I created this was for efficiency, and doing this every time kind of defeats the purpose of it.
Because of this, I tried getting data from the badge page for the bot on the game that it's playing, this is what I have so far...
else if (message.StartsWith(".updateidle"))
{
var webGet = new HtmlWeb();
var SteamID64 = Bot.SteamClient.SteamID.ConvertToUInt64();
string htmlget = "http://www.steamcommunity.com/profiles/" + SteamID64 + "/gamecards/" + newgame;
var doc = webGet.Load(htmlget);
HtmlNode hoursNode = doc.DocumentNode.SelectSingleNode("//div[#class=\"badge_title_stats_playtime\"]");
string hours = Regex.Match(hoursNode.InnerText, #"[0-9\.,]+").Value;
var cards = doc.DocumentNode.SelectSingleNode("div[#class='badge_title_stats_drops']/span").InnerText;
if (hours == string.Empty)
{
hours = "0.0";
}
Bot.SteamFriends.SendChatMessage(OtherSID, type, "I have been idling for " + hours + " hours on game " + newgame + " and have " + cards + " card drops remaining.");
}
Getting the hours works fine, if the bot has no time on that game, it doesn't appear, so I just check if it's empty then set it to 0.0, however, with the cards, it appears as either "No card drops remaining" or " card drops remaining" which it doesn't get either, I tried using the same method as the hours and only get it if it's a number, and it still returns with "0", same result goes for this...
I also tried again with doing a check if the string is empty, because that could mean there is no card drops remaining, as there would be no numbers, and I also had a look online for methods of getting span data inside a div, or span data general, and neither methods worked, they'd just return with "0". And if you can't already tell, I do have the HTML Agility Pack.

So building in my previous answer, that I have decided not to edit, since the followup here is gonna be large. I amusing both Selenium and Html Agility Pack for this. First I log in using Selenium(I am using Mono btw). After that I type in authorize my pc manually(if yours is already authorized then skip this step) and then go to the console and press any key to proceed with getting card info. I will gather the card info for all games in this case. I can't identify which game still has card drops as it has not been implemented yet.
class MainClass
{
public static void Main(string[] args)
{
string userName = "username";
string password ="password";
string steamProfile = "steamprofile";
HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
using (var driver = new FirefoxDriver())
{
// Go to the home page
driver.Navigate().GoToUrl("https://store.steampowered.com//login/?redir=0");
// Get the page elements
var userNameField = driver.FindElementById("input_username");
var userPasswordField = driver.FindElementById("input_password");
//var loginButton = driver.FindElementById("login_btn_signin");
var loginButton = driver.FindElementByXPath("//button[#class='btnv6_blue_hoverfade btn_medium']");
// Type user name and password
userNameField.SendKeys(userName);
userPasswordField.SendKeys(password);
// and click the login button
loginButton.Click();
System.Threading.Thread.Sleep(5000);
//Type authorization code and enter manually.
System.Console.ReadKey();
driver.Navigate().GoToUrl("http://steamcommunity.com/profiles/"+steamProfile+"/badges");
driver.GetScreenshot().SaveAsFile(#"screen.png", ImageFormat.Png); //Debuggin purposes, as I was first using PhantomJS
htmlDoc.LoadHtml(driver.PageSource);
Console.Clear();
}
HtmlNodeCollection col = htmlDoc.DocumentNode.SelectNodes("//span[#class='progress_info_bold']");
foreach (HtmlNode n in col)
{
Console.WriteLine(n.InnerText);
}
}
}
}
The output in my case
5 of 29 tasks completed
No card drops remaining
No card drops remaining
No card drops remaining
4 card drops remaining
3 card drops remaining
This code also gives you the badge progress. You must figure out yourself how to filter your data in Html Agility Pack(read up on xpath). I also recommend that you use Selenium, since you can start a steamgame from your webpage using it.
remember that the xpath I gave you in my first answer and is also used in the code above finds ALL("//") the that has a class that equals "progress_info_bold".

You need to be more specific about what nodes to pick. I highly disencourage you from ever using regex to try and navigate the innertext or innerhtml of an htmldocument.
To find the HTmlNodes regarding if there is anymore cards to drop. try using this xpath:
"//span[#class='progress_info_bold']"
These nodes will either contain the text:
"No card drops remaining"
or
number+" card drops remaining"

Related

Scrape data from div in Windows.Form

I am new in c# programming. I am trying to scrape data from div (I want to display temperature from web page in Forms application).
This is my code:
private void btnOnet_Click(object sender, EventArgs e)
{
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
HtmlWeb web = new HtmlWeb();
doc = web.Load("https://pogoda.onet.pl/");
var temperatura = doc.DocumentNode.SelectSingleNode("/html/body/div[1]/div[3]/div/section/div/div[1]/div[2]/div[1]/div[1]/div[2]/div[1]/div[1]/div[1]");
onet.Text = temperatura.InnerText;
}
This is the exception:
System.NullReferenceException:
temperatura was null.
You can use this:
public static bool TryGetTemperature(HtmlAgilityPack.HtmlDocument doc, out int temperature)
{
temperature = 0;
var temp = doc.DocumentNode.SelectSingleNode(
"//div[contains(#class, 'temperature')]/div[contains(#class, 'temp')]");
if (temp == null)
{
return false;
}
var text = temp.InnerText.EndsWith("°") ?
temp.InnerText.Substring(0, temp.InnerText.Length - 5) :
temp.InnerText;
return int.TryParse(text, out temperature);
}
If you use XPath, you can select with more precission your target. With your query, a bit change in the HTML structure, your application will fail. Some points:
// is to search in any place of document
You search any div that contains a class "temperature" and, inside that node:
you search a div child with "temp" class
If you get that node (!= null), you try to convert the degrees (removing '°' before)
And check:
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
HtmlWeb web = new HtmlWeb();
doc = web.Load("https://pogoda.onet.pl/");
if (TryGetTemperature(doc, out int temperature))
{
onet.Text = temperature.ToString();
}
UPDATE
I updated a bit the TryGetTemperature because the degrees are encoded. The main problem is the HTML. When you request the source code you get some HTML that browser update later dynamically. So the HTML that you get is not valid for you. It doesn't contains the temperature.
So, I see two alternatives:
You can use a browser control (in Common Controls -> WebBrowser, in the Form Tools with the Button, Label...), insert into your form and Navigate to the page. It's not difficult, but you need learn some things: wait to events for page downloaded and then get source code from the control. Also, I suppose you'll want to hide the browser control. Be carefully, sometimes the browser doesn't works correctly if you hide. In that case, you can use a visible Form outside desktop and manage activate events to avoid activate this window. Also, hide from Task Window (Alt+Tab). Things become harder in this way but sometimes is the only way.
The simple way is search the location that you want (ex: Madryt) and look in DevTools the request done (ex: https://pogoda.onet.pl/prognoza-pogody/madryt-396099). Use this Url and you get a valid HTML.

can't load scores from leaderboard

i'm setting up a leaderboard system in my unity game, using the google play games services plugin.
i want to load score in order to integrate them in my custom LeaderbordUI,
I followed the documentation and used the ILeaderboard.LoadScores but it's not working.
when i check the logcat i get this :
02-04 11:03:56.580: W/Unity(18969): !!! [Play Games Plugin DLL] 02/04/19 11:03:56 +01:00 WARNING: Error returned from fetch: -108
I have tried to loadScore with the method "Social.LoadScores" and "PlayGamesPlatform.Instance.LoadScores", but i'm getting the same warning.
PS: when i use Social.ShowLeaderboardUI() it shows me the leaderboard.
but when i use PlayGamesPlatform.Instance.ShowLeaderboardUI(LB_Stars.id) to show a specific leaderboard it gives me "hmm,something went wrong in play games"
public void LoadLeaderboard()
{
LB_Stars.LoadScores(ok =>
{
if (ok)
{
LoadUsersAndDisplay(LB_Stars);
}
else
{
Debug.Log("Error retrieving STARS leaderboard");
}
});
}
internal void LoadUsersAndDisplay(ILeaderboard lbStar)
{
Debug.Log("gonna load user and display them");
List<string> userIds = new List<string>();
foreach (IScore score in lbStar.scores)
{
userIds.Add(score.userID);
}
Social.LoadUsers(userIds.ToArray(), (users) =>
{
string status = "Leaderboard loading: " + lbStar.title + " count = " +
lbStar.scores.Length;
foreach (IScore score in lbStar.scores)
{
IUserProfile user = FindUser(users, score.userID);
if (user != null)
{
UserLeaderboardClone = Instantiate(UserLeaderboardPrefab);
UserLeaderboardClone.name = score.rank.ToString();
LeaderboardUserScript lbUser = UserLeaderboardClone.GetComponent<LeaderboardUserScript>();
lbUser.transform.SetParent(LBScrollview.content.transform, false);
FillUserInfo(lbUser, user, score);
}
}
});
}
Okay i've figured it out, based on a comment on github https://github.com/playgameservices/play-games-plugin-for-unity/issues/2045#issuecomment-350335234
After spending a couple of hours on this, my problem turned out to be my game was using leaderboard ids from another app.
So it failed with this "unauthorized error", however the exact cause was not given.
The reason why it happened was because the play games configuration in unity was caching old values. When you open it - it has the correct ids, however in the generated GPGSids.cs file - wrong ids are present.
The solution was simply to regenerate that by re-saving the configuration.

Embedding Cgi video in site

Hello All I have various web cameras i would like to embed in my site
http://81.137.212.183:4483/GetData.cgi
The problem is at times the cameras go down so i need to check they are active in c# before attempting to render:
<img height="240" width="320" src="http://81.137.212.183:4483/GetData.cgi" />
Please can someone advise how i can check the cgi is active in c# or any other recommendation. If i simple load the cgi and it is down it causes the browser to crash
One recommendation was to use the code below:
The problem with the below approach is the site is forever loading and a fav icon is never shown as can be seen http://www.camsecure.co.uk/
newImage = new Image();
function LoadNewImage() {
var unique = new Date();
document.images.webcam.src = newImage.src;
newImage.src = "http://collectart.dyndns.org:4484/Jpeg/CamImg.jpg?time=" + unique.getTime();
}
function InitialImage() {
var unique = new Date();
newImage.onload = LoadNewImage();
newImage.src = "http://collectart.dyndns.org:4484/Jpeg/CamImg.jpg?time=" + unique.getTime();
document.images.webcam.src = "http://collectart.dyndns.org:4484/Jpeg/CamImg.jpg?time=" + unique.getTime();
document.images.webcam.onload = "";
}
First off, you need to put some security over that first link. It appears the camera settings are public and available to anyone.
If the only problem is the long loading times slowing the rest of the site down, you could load the images in an iframe rather than directly in an image tag -- then the hang is only in the iframe:
<iframe src="http://81.137.212.183:4483/Simple/home.htm?IMG"></iframe>
To check the IP camera is up, you could simply try to get it's host page:
using System.Net.Http;
...
var uri = new Uri("http://81.137.212.183:4483/Simple/index.htm");
var task = new HttpClient().GetAsync(uri);
if (task.Wait(TimeSpan.FromSeconds(1)) && task.Result.IsSuccessStatusCode)
{
// SUCCESS!
}
else
{
// FAILURE... try next camera
}
However, it looks like the image .cgi location can still fail if the camera is available. In that case it would be best to load in an iframe even if you get success.

Delving into the world of XML (Windows Phone) Error I dont understand (The ' ' character, hexadecimal value 0x20, cannot be included in a name.)

So I am starting to learn how to use XML data within a app and decided to use some free data to do this however I cannot for the life of me get it working this is my code so far. (I have done a few apps with static data before but hey apps are designed to use the web right? :p)
public partial class MainPage : PhoneApplicationPage
{
List<XmlItem> xmlItems = new List<XmlItem>();
// Constructor
public MainPage()
{
InitializeComponent();
LoadXmlItems("http://hatrafficinfo.dft.gov.uk/feeds/datex/England/CurrentRoadworks/content.xml");
test();
}
public void test()
{
foreach (XmlItem item in xmlItems)
{
testing.Text = item.Title;
}
}
public void LoadXmlItems(string xmlUrl)
{
WebClient client = new WebClient();
client.OpenReadCompleted += (sender, e) =>
{
if (e.Error != null)
return;
Stream str = e.Result;
XDocument xdoc = XDocument.Load(str);
***xmlItems = (from item in xdoc.Descendants("situation id")
select new XmlItem()
{
Title = item.Element("impactOnTraffic").Value,
Description = item.Element("trafficRestrictionType").Value
}).ToList();***
// close
str.Close();
// add results to the list
xmlItems.Clear();
foreach (XmlItem item in xmlItems)
{
xmlItems.Add(item);
}
};
client.OpenReadAsync(new Uri(xmlUrl, UriKind.Absolute));
}
}
I am basically trying to learn how to do this at the moment as I am intrigued how to actually do it (I know there are many ways but ATM this way seems the easiest) I just don't get what the error is ATM. (The bit in * is where it says the error is)
I also know the display function ATM is not great (As it will only show the last item) but for testing this will do for now.
To some this may seem easy, as a learner its not so easy for me just yet.
The error in picture form:
(It seems I cant post images :/)
Thanks in advance for the help
Edit:
Answer below fixed the error :D
However still nothing is coming up. I "think" it's because of the XML layout and the amount of descendants it has (Cant work out what I need to do being a noob at XML and pulling it from the web as a data source)
Maybe I am starting too complicated :/
Still any help/tips on how to pull some elements from the feed (As there all in Descendants) correctly and store them would be great :D
Edit2:
I have it working (In a crude way) but still :D
Thanks Adam Maras!
The last issue was the double listing. (Adding it to a list, to then add it to another list was causing a null exception) Just using the 1 list within the method solved this issue, (Probably not the best way of doing it but it works for now) and allowed for me to add the results to a listbox until I spend some time working out how to use ListBox.ItemTemplate & DataTemplate to make it look more appealing. (Seems easy enough I say now...)
Thanks Again!!!
from item in xdoc.Descendants("situation id")
// ^
XML tag names can't contain spaces. Looking at the XML, you probably just want "situation" to match the <situation> elements.
After looking at your edit and further reviewing the XML, I figured out what the problem is. If you look at the root element of the document:
<d2LogicalModel xmlns="http://datex2.eu/schema/1_0/1_0" modelBaseVersion="1.0">
You'll see that it has a default namespace applied. The easiest solution to your problem will be to first get the namespsace from the root element:
var ns = xdoc.Root.Name.Namespace;
And then apply it wherever you're using a string to identify an element or attribute name:
from item in xdoc.Descendants(ns + "situation")
// ...
item.Element(ns + "impactOnTraffic").Value
item.Element(ns + "trafficRestrictionType").Value
One more thing: <impactOnTraffic> and <trafficRestrictionType> aren't direct children of the <situation> element, so you'll need to change that code as well:
Title = items.Descendants(ns + "impactOnTraffic").Single().Value,
Description = item.Descendants(ns + "trafficRestrictionType").Single().Value

Setting up 3DCart API with a C# App

I have been trying to create an application to go through our database at a set interval and update/add any new items to 3DCarts database. Their code example uses soap in an xml file to send 1 request per call. So I need to to be able to generate the xml I need with the items information on the fly before sending it. I have done hardly anything with XML files like this and cannot figure out how to create the chunk of code I need and send it. One method that has been suggested is create a file but still executing has been a problem and would be very inefficient for a large number of items. Here is what I have so far
sqlStatement = "SELECT * FROM products WHERE name = '" + Convert.ToString(reader.GetValue(0)) + "'";
ServiceReferenceCart.cartAPIAdvancedSoapClient bcsClient = new ServiceReferenceCart.cartAPIAdvancedSoapClient();
ServiceReferenceCart.runQueryResponse bcsResponse = new ServiceReferenceCart.runQueryResponse();
bcsClient.runQuery(storeUrl, userKey, sqlStatement, callBackURL);
string result = Convert.ToString(bcsResponse);
listBox1.Items.Add(result);
EDIT: Changed from sample code block to current code block as I got a service reference setup finally. They provide no details though for using the functions in the reference. With this bcsResponse is just a blank, when I try adding .Body I have the same result but when I add .runQuery to the .Body I get a "Object reference not set to an instance of an object." error. As I have said I have not messed with service references before.
I hope I have explained well enough I just really have not worked with this kind of stuff before and it has become extremely frustrating.
Thank you in advance for any assistance.
I actually ended up figuring this out after playing around with it. Here is what I did to get the reference to work. This may have been easy for anyone who have used the references before but I have not and have decided to post this in case anyone else has this problem. The SQL can be SELECT, ADD, UPDATE and DELETE statements this was to see if the sku was listed before updating/adding.
//Will be using these multiple times so a variable makes more sense
// DO NOT include http:// in the url, also id is not shown in their
//database layout pdf they will give but it is the sku/product number
string sqlStatement = "SELECT id FROM products WHERE id = '" + Convert.ToString(reader.GetValue(0)) + "')))";
string userKey = "YourKeyHere";
string storeUrl = "YourStoresURLHere";
// Setting up instances from the 3DCart API
cartAPIAdvancedSoapClient bcsClient = new cartAPIAdvancedSoapClient();
runQueryRequest bcsRequest = new runQueryRequest();
runQueryResponse bcsResponse = new runQueryResponse();
runQueryResponseBody bcsRespBod = new runQueryResponseBody();
runQueryRequestBody bcsReqBod = new runQueryRequestBody();
//assigning required variables to the requests body
bcsReqBod.storeUrl = storeUrl;
bcsReqBod.sqlStatement = sqlStatement;
bcsReqBod.userKey = userKey;
//assigning the body to the request
bcsRequest.Body = bcsReqBod;
//Setting the response body to be the result
bcsRespBod.runQueryResult = bcsClient.runQuery(bcsReqBod.storeUrl, bcsReqBod.userKey, bcsReqBod.sqlStatement, bcsReqBod.callBackURL );
bcsResponse.Body = bcsRespBod;
//adding the result to a string
string result = bcsResponse.Body.runQueryResult.Value;
//displaying the string, this for me was more of a test
listBox1.Items.Add(result);
You will also need to activate the Advanced API on your shop as you may notice there is no actual option as the pdf's say, you need to go to their store and purchase(its free) and wait for them to activate it. This took about 2 hrs for us.

Categories