Hello i'm trying to implement Facebook friends tracker that can track who's online and who's offline using Selenium WebDriver.
Everything works fine except when a user goes offline and i remove him from the collection it throws an exception .
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.Support.Events;
using OpenQA.Selenium.Support.UI;
namespace FacebookFriendTracker
{
public delegate void StatusChangedEventHandler(object source, StatusChangedEventArgs e);
public class Watcher
{
private readonly IWebDriver _driver;
private HashSet<User> _tempUserOnline = new HashSet<User>();
public event StatusChangedEventHandler StatusChanged;
private bool run;
public Watcher()
{
//UsersOnline = new HashSet<User>();
_driver = new EdgeDriver();
_driver.Navigate().GoToUrl("https://mbasic.facebook.com");
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(20));
wait.Until(ExpectedConditions.ElementExists(By.PartialLinkText("Chat")));
var element = _driver.FindElement(By.PartialLinkText("Chat"));
element.Click();
run = false;
}
public void Tracker()
{
Thread.Sleep(5000);
//_usersOnline = _driver.FindElements(By.XPath(".//a[contains(#href,'fbid')]"));
var usersOnline = new HashSet<User>();
foreach (var userOnline in _driver.FindElements(By.XPath(".//a[contains(#href,'fbid')]")))
{
var extracedAttributeValue = userOnline.GetAttribute("href");
var regex = new Regex(#"\d+");
var id = long.Parse(regex.Match(extracedAttributeValue).Value);
var fullName = userOnline.Text;
usersOnline.Add(new User() {FullName = fullName, Id = id});
}
while (true)
{
Thread.Sleep(5000);
_driver.Navigate().Refresh();
var newUsersOnline = new HashSet<User>();
foreach (var user in _driver.FindElements(By.XPath(".//a[contains(#href,'fbid')]")))
{
var attirbute = user.GetAttribute("href");
var reg = new Regex(#"\d+");
var newId = long.Parse(reg.Match(attirbute).Value);
var newFullName = user.Text;
newUsersOnline.Add(new User() { FullName = newFullName, Id = newId });
}
_tempUserOnline = usersOnline;
foreach (var usrOnline in newUsersOnline.Except(_tempUserOnline))
{
OnStatusChanged(this , new StatusChangedEventArgs() {User = usrOnline,Status = Status.Online});
_tempUserOnline.Add(usrOnline);
}
// Here it throws and exception if the the user goes offline
foreach (var usroffline in usersOnline.Except(newUsersOnline))
{
OnStatusChanged(this, new StatusChangedEventArgs() { User = usroffline, Status = Status.Offline });
_tempUserOnline.Remove(usroffline);
}
}
}
protected virtual void OnStatusChanged(object source, StatusChangedEventArgs e)
{
if (StatusChanged !=null)
OnStatusChanged(source,e);
else
{
Console.WriteLine("User: {0} is {1} ",e.User.FullName,e.Status);
}
}
}
}
You cannot modify that collection while an emueration is in progress. A common fix is to ToList() it:
foreach (var usroffline in usersOnline.Except(newUsersOnline).ToList())
{
OnStatusChanged(this, new StatusChangedEventArgs() { User = usroffline, Status = Status.Offline });
_tempUserOnline.Remove(usroffline);
}
The problem is that you can't modify a collection while you're iterating through it with a foreach loop.
Here, you are setting _tempUserOnline to point to the same object as usersOnline:
_tempUserOnline = usersOnline;
Then further down, you have this loop, which modifies _tempUserOnline, thus modifying usersOnline also since they point to the same object. Since you are looping through usersOnline (and effectively _tempUserOnline too), you are getting the error when you do _tempUserOnline.Remove(usroffline).
foreach (var usroffline in usersOnline.Except(newUsersOnline))
{
OnStatusChanged(this, new StatusChangedEventArgs() { User = usroffline, Status = Status.Offline });
_tempUserOnline.Remove(usroffline);
}
Did you intend to copy usersOnline into tempUserOnline? If so, you should use .CopyTo() or something equivalent to make an actual copy:
User[] _tempUserOnline = new User[usersOnline.Count - 1];
usersOnline.CopyTo(_tempUserOnline);
Related
i am using a method to retrieve data from an OPC DA server using TitaniumAS packages, the problem i am having is that i have a lot of tags to read/write so i have to use methods.
The WriteX method works fines as it doesnt have to return anything but the read does not, well it does its job, it reads but i cannot use that data outside of the method because it was a void method, when i tried to use it as a String method (that's the type of data i need) it says :
Error CS0161 'ReadX(string, string)': not all code paths return a value
PS : note that i am just a beginner in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TitaniumAS.Opc.Client.Common;
using TitaniumAS.Opc.Client.Da;
using TitaniumAS.Opc.Client.Da.Browsing;
using System.Threading;
using System.Threading.Channels;
using Async;
namespace OPCDA
{
class Program
{
static void Main(string[] args)
{
TitaniumAS.Opc.Client.Bootstrap.Initialize();
Uri url = UrlBuilder.Build("Kepware.KEPServerEX.V6");
using (var server = new OpcDaServer(url))
{
server.Connect();
OpcDaGroup group = server.AddGroup("MyGroup");
group.IsActive = true;
Ascon ascon1 = new Ascon();
ReadX("Channel1.Ascon1.AsconS", ascon1.ALM);
Console.WriteLine("value = {0}", ascon1.ALM);
void WriteX(String Link, String Ascon)
{
var definition1 = new OpcDaItemDefinition
{
ItemId = Link,
IsActive = true
};
OpcDaItemDefinition[] definitions = { definition1 };
OpcDaItemResult[] results = group.AddItems(definitions);
OpcDaItem tag = group.Items.FirstOrDefault(i => i.ItemId == Link);
OpcDaItem[] items = { tag };
object[] Values = { Ascon };
HRESULT[] Results = group.Write(items, Values);
}
string ReadX(String Link, String read)
{
var definition1 = new OpcDaItemDefinition
{
ItemId = Link,
IsActive = true
};
OpcDaItemDefinition[] definitions = { definition1 };
OpcDaItemResult[] results = group.AddItems(definitions);
OpcDaItemValue[] values = group.Read(group.Items, OpcDaDataSource.Device);
read = Convert.ToString(values[0].Value);
}
}
}
}
}
the first step was to state the return like this :
return Convert.ToString(values[0].Value) instead of read = Convert.ToString(values[0].Value)
then go up and use that value with my variable :
ascon1.ALM=ReadX("Channel1.Ascon1.AsconS");
I make SOAP requests to an e-commerce API to know how many orders were made for a specified product, in a given time frame.
My method takes a product ID and a number of days to create a time frame. Then it constructs a SOAP request and returns the amount sold products.
public static async Task<int?> GetOrdersFromApi(int providedId, int days) {
var dateFrom = DateTime.Now.AddDays(days * -1).ToString("yyyy-MM-dd") + " 00:00:00";
var dateTo = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
float orderedCount = 0;
int orderedCountToPass = 0;
int i = 0;
var binding = new BasicHttpBinding {
MaxReceivedMessageSize = 40000000
};
var address = new EndpointAddress("http://example.com/api/get/");
using (var client = new ApiOrdersPortTypeClient(binding, address)) {
try {
while (true) {
// request parameters
var request = new ApiOrdersGetAsync.requestType {
authenticate = new ApiOrdersGetAsync.authenticateType {
userLogin = "Username",
authenticateKey = "Key",
}
};
request.#params = new ApiOrdersGetAsync.paramsType {
ordersStatuses = new string[] { "finished" },
products = new productType[1],
};
request.#params.products[0] = new productType {
productIdSpecified = true,
productId = providedId,
};
request.#params.ordersRange = new ordersRangeType {
ordersDateRange = new ordersDateRangeType()
};
request.#params.ordersRange.ordersDateRange.ordersDateTypeSpecified = true;
request.#params.ordersRange.ordersDateRange.ordersDateType = ordersDateTypeType.dispatch;
request.#params.ordersRange.ordersDateRange.ordersDateBegin = dateFrom;
request.#params.ordersRange.ordersDateRange.ordersDateEnd = dateTo;
request.#params.resultsPageSpecified = true;
request.#params.resultsPage = i;
// processing the result
var results = await client.getAsync(request);
foreach (var result in results.Results) {
int productsResultsPage = 0;
foreach (var product in result.orderDetails.productsResults) {
try {
if (result.orderDetails.productsResults[productsResultsPage ].productId == providedId) {
orderedCount += result.orderDetails.productsResults[y].productQuantity;
productsResultsPage++;
}
} catch (IndexOutOfRangeException ex) {
// error is thrown to escape loop - sloppy, I know
Console.WriteLine(ex);
};
}
};
orderedCountToPass = (int)orderedCount;
orderedCount = 0;
i++;
};
} catch (NullReferenceException) {
// do nothing, we just want to exit while loop when i is higher than page count
}
return orderedCountToPass;
};
}
The result often should be in hundreds, but regardless how well a product sells, it returns something from 0 to 4.
Here is a sample response:
For example, I'm only interested with productId == 479, but an order was made with other products as well that don't interest me - I need to filter them.
I'm doing something wrong with how I try to filter results. How do I do it properly? I'm certain the request is correct and response does contain all possible orders.
You are getting xml results so use Net Xml library :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string URL = #"Enter URL Here";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(URL);
List<XElement> items = doc.Descendants("item").ToList();
}
}
}
I'm trying to find all the builds and releases that are configured to use a specific agent pool, using the .NET Client Libraries.
Assuming agentPoolId, I can get all the build definitions like this:
// _connection is of type VssConnection
using (var buildClient = _connection.GetClient<BuildHttpClient>())
{
List<BuildDefinitionReference> allBuilds = await buildClient.GetDefinitionsAsync(projectName, top: 1000, queryOrder: DefinitionQueryOrder.DefinitionNameAscending);
List<BuildDefinitionReference> builds = allBuilds.Where(x => HasAgentPoolId(x, agentPoolId)).ToList();
}
private bool HasAgentPoolId(BuildDefinitionReference buildDefinition, int agentPoolId)
{
TaskAgentPoolReference pool = buildDefinition?.Queue?.Pool;
if (pool == null)
{
return false;
}
return pool.Id.Equals(agentPoolId);
}
But I couldn't find a way to find the release definitions that have one or more environments configured to use a particular agent. Any suggestion?
I was manged to get all releases by Agent Pool ID via Rest Api and not via NET Client Libraries.Hope that helps.
C# Code snippet:
public class ReleaseResponse
{
[JsonProperty("value")]
public List<ReleaseItem> Value { get; set; }
}
public class ReleaseItem
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("Id")]
public int Id { get; set; }
}
static void Main(string[] args)
{
string tfsURL = "TFS URL";
string releaseDefurl = $"{tfsURL}/_apis/release/definitions?$expand=artifacts&api-version=3.2-preview.3";
const int agentPoolID = "AGENT Pool ID";
List<string> relevantReleases = new List<string>();
WebClient client = new WebClient();
client.UseDefaultCredentials = true;
client.Headers.Add("Content-Type", "application/json");
var releaseList = client.DownloadString(releaseDefurl);
var allReleases = JsonConvert.DeserializeObject<ReleaseResponse>(releaseList).Value;
foreach (var release in allReleases)
{
string releaseInfoApi = $"{tfsURL}/_apis/Release/definitions/{release.Id}";
var getReleseInfo = client.DownloadString(releaseInfoApi);
var releaseInfo = JsonConvert.DeserializeObject<TFSLogic.RootObject>(getReleseInfo);
var deploymentAgents = releaseInfo.environments.ToList().Where(e => e.deployPhases.FirstOrDefault().deploymentInput.queueId == agentPoolID).Count();
if (deploymentAgents > 0)
{
relevantReleases.Add(release.Name);
}
}
}
Find TFSLogic here : https://codebeautify.org/online-json-editor/cb7aa0d9
Powershell Code snippet:
$tfsUrl = "TFS URL"
$releaseDefurl = $tfsUrl + '/_apis/release/definitions?$expand=artifacts&api-version=3.2-preview.3'
$agentPoolID = "Agent Pool ID"
$relevantReleases = #();
$allReleasesID = (Invoke-RestMethod -Uri ($releaseDefurl) -Method Get -UseDefaultCredentials).value.id
function getReleaseByAgentPoolID($releaseID,$agentPoolID)
{
$ReleaseInfo = Invoke-RestMethod -Uri "$tfsUrl/_apis/Release/definitions/$releaseID" -Method Get -UseDefaultCredentials
$deploymentAgents = $ReleaseInfo.environments | % {$_.deployPhases.deploymentInput.queueId} | where {$_ -eq $agentPoolID}
if($deploymentAgents.Count -gt 0)
{
return $ReleaseInfo.name
}
}
foreach ($releaseID in $allReleasesID)
{
$relevantReleases += getReleaseByAgentPoolID -releaseID $releaseID -agentPoolID $agentPoolID
}
UPDATE :
It took me some time,But i was able to achieve that with azure-devops-dotnet-samples
I hope this example is finally what you are looking for.
using Microsoft.VisualStudio.Services.WebApi;
using System;
using System.Linq;
using Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.Clients;
using Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.Contracts;
using Microsoft.VisualStudio.Services.Common;
using System.Collections.Generic;
namespace FindReleaseByAgentPoolID
{
class Program
{
const int agentPoolID = 999;
static void Main(string[] args)
{
var relevantReleases = new List<string>();
VssCredentials c = new VssCredentials(new WindowsCredential(System.Net.CredentialCache.DefaultNetworkCredentials));
var tfsURL = new Uri("TFS URL");
var teamProjectName = "PROJECT";
using (var connection = new VssConnection(tfsURL, c))
using (var rmClient = connection.GetClient<ReleaseHttpClient2>())
{
var releases = rmClient
.GetReleaseDefinitionsAsync(teamProjectName, string.Empty, ReleaseDefinitionExpands.Environments)
.Result.ToArray();
foreach (var release in releases)
{
var r = rmClient.GetReleaseDefinitionAsync(teamProjectName, release.Id);
var deploymentAgents = r.Result.Environments.SelectMany(e =>
e.DeployPhases.Select(dp =>
dp.GetDeploymentInput()).Cast<DeploymentInput>()).Where(di =>
di.QueueId == agentPoolID).Count();
if (deploymentAgents > 0)
{
relevantReleases.Add(release.Name);
}
}
}
}
}
}
Found a solution, many thanks to #amit-baranes for pointing me in the right direction.
I've changed his code sample to use the await keyword instead of using .Result, and use .OfType<DeploymentInput>() instead of .Cast<DeploymentInput>() (it was throwing some exceptions).
Oh, and the most important thing I've learned: agent pool ID and queue ID are different things!!! If you intend to use the agent pool ID to get the release definitions you'll need to get the correspondent agent queue.
Code sample:
// set agent pool Id and project name
int agentPoolId = 123456;
string teamProjectName = ".....";
// _connection is of type VssConnection
using (var taskAgentClient = _connection.GetClient<TaskAgentHttpClient>())
using (var releaseClient = _connection.GetClient<ReleaseHttpClient2>())
{
// please note: agent pool Id != queue Id
// agent pool id is used to get the build definitions
// queue Id is used to get the release definitions
TaskAgentPool agentPool = await taskAgentClient.GetAgentPoolAsync(agentPoolId);
List<TaskAgentQueue> queues = await taskAgentClient.GetAgentQueuesByNamesAsync(teamProjectName, queueNames: new[] { agentPool.Name });
TaskAgentQueue queue = queues.FirstOrDefault();
List<ReleaseDefinition> definitions = await releaseClient.GetReleaseDefinitionsAsync(teamProjectName, string.Empty, ReleaseDefinitionExpands.Environments);
foreach (ReleaseDefinition definition in definitions)
{
var fullDefinition = await releaseClient.GetReleaseDefinitionAsync(teamProjectName, definition.Id);
bool hasReleasesWithPool = fullDefinition.Environments.SelectMany(GetDeploymentInputs)
.Any(di => di.QueueId == queue.Id);
if (hasReleasesWithPool)
{
Debug.WriteLine($"{definition.Name}");
}
}
}
private IEnumerable<DeploymentInput> GetDeploymentInputs(ReleaseDefinitionEnvironment environment)
{
return environment.DeployPhases.Select(dp => dp.GetDeploymentInput())
.OfType<DeploymentInput>();
}
I wish to automate the transfer of ether to a list of people.
Assume the list is in a csv.
I wrote some code to automate the process.
class Program
{
int nonce = 0;
static void Main(string[] args)
{
var account = SetupAccount();
var recipients = ReadCsv();
var web3Init = GetConnection();
nonce = web3.Eth.Transactions.GetTransactionCount.SendRequestAsync(account.Address).Result;
//var recipients = new List<Records>() { new Records() { Value = 10000000000000000, Address = "0x5CC494843e3f4AC175A5e730c300b011FAbF2cEa" } };
foreach (var recipient in recipients)
{
try
{
var web3 = GetConnection();
var receipt = SendEther(account, recipient, web3).Result;
}
catch (System.Exception)
{
MessageBox.Show("Failed");
}
Thread.Sleep(30000);
}
}
private static async Task<TransactionReceipt> SendEther(Account account, Records recipient, Web3 web3)
{
var transactionPolling = web3.TransactionManager.TransactionReceiptService;
//var currentBalance = await web3.Eth.GetBalance.SendRequestAsync(account.Address);
//assumed client is mining already
//when sending a transaction using an Account, a raw transaction is signed and send using the private key
return await transactionPolling.SendRequestAndWaitForReceiptAsync(() =>
{
var transactionInput = new TransactionInput
{
From = account.Address,
//Gas = new HexBigInteger(25000),
GasPrice = new HexBigInteger(10 ^ 10),
To = recipient.Address,
Value = new HexBigInteger(new BigInteger(recipient.Value)),
Nonce = nonce
};
var txSigned = new Nethereum.Signer.TransactionSigner();
var signedTx = txSigned.SignTransaction(account.PrivateKey, transactionInput.To, transactionInput.Value, transactionInput.Nonce);
var transaction = new Nethereum.RPC.Eth.Transactions.EthSendRawTransaction(web3.Client);
nonce++;
return transaction.SendRequestAsync(signedTx);
});
}
private static Web3 GetConnection()
{
return new Web3("https://mainnet.infura.io");
}
private static Account SetupAccount()
{
var password = "#Password";
var accountFilePath = #"filePath";
return Account.LoadFromKeyStoreFile(accountFilePath, password);
}
private static List<Records> ReadCsv()
{
string filePath = #"C:\Users\Potti\source\repos\ConversionFiles\XrcfRecipients.csv";
if (File.Exists(filePath))
{
using (StreamReader stream = new StreamReader(filePath))
{
CsvReader reader = new CsvReader(stream, new Configuration
{
TrimOptions = TrimOptions.Trim,
HasHeaderRecord = true,
HeaderValidated = null
});
reader.Configuration.RegisterClassMap<RecordMapper>();
return reader.GetRecords<Records>().ToList();
}
}
else
{
return null;
}
}
}
class Records
{
public string Address { get; set; }
public decimal Value { get; set; }
}
sealed class RecordMapper : ClassMap<Records>
{
public RecordMapper()
{
Map(x => x.Address).Name("Address");
Map(x => x.Value).Name("Value");
}
}
How do i modify the process to execute all the transactions at once instead of waiting for each to complete? (Fire and forget)
Also, are there any security considerations of doing this?
What you are currently doing is waiting for each transaction to be mined. What you can do is the following:
var account = new Account("privateKey"); // or load it from your keystore file as you are doing.
var web3 = new Web3(account, "https://mainnet.infura.io");
First create a web3 instance using the same Account object, because we are using an account with a private key, Nethereum will sign your transactions offline before sending them.
Now using the TransactionManager, you can then send a transaction per each recepient
var transactionHashes = new List<string>();
foreach(var recepient in recepients){
var transactionInput = new TransactionInput
{
From = account.Address,
GasPrice = Web3.Convert.ToWei(1.5, UnitConversion.EthUnit.Gwei);,
To = recipient.Address,
Value = new HexBigInteger(new BigInteger(recipient.Value)),
};
var transactionHash = web3.Eth.TransactionManager.SendTransactionAsync(transactionInput);
transanctionHashes.Add(transactionHash);
}
Note that when Nethereum uses the same instance of an Account and TransactionManager (or Web3 in this scenario) it creates a default NonceMemoryService, so you don't need to keep track of your nonces (Transaction number), to sign the transaction.
Also I have done a conversion for the GasPrice from Gwei to Wei, as an example of Unit conversions, I assume that you already have converted to Wei the Ether amounts you are going to send.
Finally, another note, to further simplify this, there is an upcoming EtherTransferService which allows you to input Ether amounts and Gwei price amounts to avoid doing conversions. Also the gas price will be calculated for you, if not passed any parameter.
web3.Eth.GetEtherTransferService().TransferEtherAsync("toAddress", EtherAmount);
My code below holds a lock on the input file preventing me from working with it later:
public static void ToWave_WMF(string source, string destination)
{
using (var reader = new MediaFoundationReader(source))
using (var rateSampler = new MediaFoundationResampler(reader, new WaveFormat(DefaultEncoding.SampleRate, reader.WaveFormat.Channels)))
using (var channelSampler = new MediaFoundationResampler(rateSampler, new WaveFormat(rateSampler.WaveFormat.SampleRate, DefaultEncoding.Channels)))
{
WaveFileWriter.CreateWaveFile(destination, channelSampler);
}
}
public static string BuildWavFile(string userFileLocation)
{
var sampleList = new List<ISampleProvider>();
try
{
// Add input file
var waveFile = AudioHelpers.ToWave_WMF(userFileLocation);
sampleList.Add(new AudioFileReader(waveFile));
WaveFileWriter.CreateWaveFile16(sirenWaveFile, new ConcatenatingSampleProvider(sampleList));
}
finally
{
foreach (var sample in sampleList)
{
((AudioFileReader)sample).Dispose();
}
}
return sirenWaveFile;
}
Am I using resources wrong? Why is this lock being held? If I delete the file after toWave_WMF() there is no issue. If I use sampleList.Add(new AudioFileReader(userFileLocation)); I also dont have any issues deleting the userFile.