# 2019-Feb-25. Created by Maxim T., Adjusted by x 2022-01-26

## Lines below might be changed by users. Any double quote inside the script should be "escaped", that is `" instead of ". 
## #############################################################################################################################################################################################
## <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< This will need to be changed every time Yahoo changes where price is located. So added psConfig parameters where values can be changed without changing script
$startStr1 = "data-field=`"regularMarketPrice`" data-trend=`"none`" data-pricehint=`"2`" value=`""; $endStr1 = "`"";
$startStr2 = "data-field=`"regularMarketPrice`" data-trend=`"none`" data-pricehint=`"4`" value=`""; $endStr2 = "`"";
$startStr3 = ",`"currentPrice`":{`"raw`":";                                                         $endStr3 = ",";
## #############################################################################################################################################################################################


$scriptPath = Split-Path -parent $MyInvocation.MyCommand.Definition; 
. ($scriptPath + "\psFunctions.ps1");     # Adding script with reusable functions
. ($scriptPath + "\psSetVariables.ps1");  # Adding script to assign values to common variables

$logFile        = $scriptPath + "\Log\" + $MyInvocation.MyCommand.Name.Replace(".ps1",".txt"); 
(Get-Date).ToString("HH:mm:ss") + " --- Starting script " + $MyInvocation.MyCommand.Name | Out-File $logFile -Encoding OEM; # starting logging to file.
$logSummary = (Get-Date).ToString("HH:mm:ss") + " Script: ExchRate YahooID".PadRight(28);
Get-ChildItem -Path $currExchIDFolder -Filter *Intraday.txt -File | foreach { $_.Delete()}; # Delete existing all files from intraday folder
$currExchIDFile = $currExchIDFolder + "YahooCurrExchIntraday.txt"; #if (Test-Path $currExchIDFile) { Remove-Item $currExchIDFile;} # Removing intraday file before each load
$reqFailed=0; $reqSucceed=0; $reqUsed=0;

$listStart = $config.IndexOf("<Currency>"); $listEnd = $config.IndexOf("</Currency>"); 
if ($listStart -eq -1 -or $listEnd -eq -1 -or $listStart+1 -ge $listEnd) {"<currency> Symbol list is empty. Exiting script." | Out-File $logFile -Encoding OEM -Append; exit(1);}
$currList = @($config | Select-Object -Index(($listStart+1)..($listEnd-1))); #list of symbols we will work on
$currCount= $currList.Count; "Currency count: $currCount. MinDate: $minDate" | Out-File $logFile -Encoding OEM -Append;
if ($currCount -le 1) {"Found just one currency, will not create currency exchange database"; exit(1);}

#$urlBase = "https://finance.google.com/finance?q=@@CURRFROM@@@@CURRTO@@"; # Example: https://finance.google.com/finance?q=USDEUR
$urlBase = "https://finance.yahoo.com/quote/@@CURRFROM@@@@CURRTO@@=X"; # Example: https://finance.yahoo.com/quote/CADUSD=X
$logSymbolFolder = $scriptPath + "\Log\SymbolWebpage"; if (!(Test-Path $logSymbolFolder)) {New-Item $logSymbolFolder -type directory}; # setting up folder where text output per currency pair will be temporary stored;

For($currFromIndex=0; $currFromIndex -lt $currCount; $currFromIndex++) {
    For($currToIndex=0; $currToIndex -lt $currCount; $currToIndex++) {
        if ($currList[$currFromIndex] -ne $currList[$currToIndex]) {
            $currFrom=$currList[$currFromIndex]; $currTo = $currList[$currToIndex]; $url = $urlBase.Replace("@@CURRFROM@@",$currFrom).Replace("@@CURRTO@@",$currTo); $currencyPair=$currFrom + "-" + $currTo;
            "  Requesting url: " + $url | Out-File $logFile -Encoding OEM -Append;
            $wc = new-object system.net.WebClient;
            try {$webpage = $wc.DownloadData($url); } # Get page from url. This page will contain all quotes for single Symbol
            catch { $reqFailed++; "  " + $currFrom-$currTo + " - Not Found (web err) `r`n" | Out-File $logFile -Encoding OEM -Append; continue;} # if attempt to get webpage failed go to next symbol
            
            $quotesTxt = [System.Text.Encoding]::ASCII.GetString($webpage); # This variable now contains downloaded quotes
            $quotesTxt | Out-File ($logSymbolFolder + "\currency_" + $currencyPair + ".txt") -Encoding OEM ; # Saving web page to \Log\SymbolWebpage\<Symbol>.txt file for review later (if needed)

            # ---------------------------------------------
            # Identifying price
            # ---------------------------------------------
            $priceTxt=""; 
            # attempting to find price between values in $startStr1 and $endStr1 strings
            $startStr = $startStr1; $endStr = $endStr1;
            if ($priceTxt -eq "") { # If price not identified YET
                if ($quotesTxt.Contains($startStr)) {
                    $quotesTxt1 = $quotesTxt.Substring($quotesTxt.IndexOf($startStr) + $startStr.Length,500); # This now includes price and date
                    if ($quotesTxt1.Contains($endStr)) {
                        $priceTxt = $quotesTxt1.Substring(0,$quotesTxt1.IndexOf($endStr)).Replace(",",""); "  Found price: $priceTxt. Price found between strings '$startStr' and '$endStr'" | Out-File $logFile -Encoding OEM -Append; 
                    } else {"  ** For Currency pair: " + $currencyPair + " start string '$startStr' found but end string'$endStr' not found. `r`n" | Out-File $logFile -Encoding OEM -Append;}
                } else {"  ** For Currency pair: " + $currencyPair + " start string '$startStr' not found. `r`n" | Out-File $logFile -Encoding OEM -Append;}
            }; 

            # attempting to find price between values in $startStr2 and $endStr2 strings
            $startStr = $startStr2; $endStr = $endStr2;
            if ($priceTxt -eq "") { # If price not identified YET
                if ($quotesTxt.Contains($startStr)) {
                    $quotesTxt1 = $quotesTxt.Substring($quotesTxt.IndexOf($startStr) + $startStr.Length,500); # This now includes price and date
                    if ($quotesTxt1.Contains($endStr)) {
                        $priceTxt = $quotesTxt1.Substring(0,$quotesTxt1.IndexOf($endStr)).Replace(",",""); "  Found price: $priceTxt. Price found between strings '$startStr' and '$endStr'" | Out-File $logFile -Encoding OEM -Append; 
                    } else {"  ** For Currency pair: " + $currencyPair + " start string '$startStr' found but end string'$endStr' not found. `r`n" | Out-File $logFile -Encoding OEM -Append;}
                } else {"  ** For Currency pair: " + $currencyPair + " start string '$startStr' not found. `r`n" | Out-File $logFile -Encoding OEM -Append;}
            };  

            # attempting to find price between values in $startStr3 and $endStr3 strings
            $startStr = $startStr3; $endStr = $endStr3;
            if ($priceTxt -eq "") { # If price not identified YET
                if ($quotesTxt.Contains($startStr)) {
                    $quotesTxt1 = $quotesTxt.Substring($quotesTxt.IndexOf($startStr) + $startStr.Length,500); # This now includes price and date
                    if ($quotesTxt1.Contains($endStr)) {
                        $priceTxt = $quotesTxt1.Substring(0,$quotesTxt1.IndexOf($endStr)).Replace(",",""); "  Found price: $priceTxt. Price found between strings '$startStr' and '$endStr'" | Out-File $logFile -Encoding OEM -Append; 
                    } else {"  ** For Currency pair: " + $currencyPair + " start string '$startStr' found but end string'$endStr' not found. `r`n" | Out-File $logFile -Encoding OEM -Append;}
                } else {"  ** For Currency pair: " + $currencyPair + " start string '$startStr' not found. `r`n" | Out-File $logFile -Encoding OEM -Append;}
            };


            # at this point value in $priceTxt should be numeric price value                
            if ($priceTxt  -eq "" -or $priceTxt  -eq "N/A" -or $priceTxt -eq "0" -or $priceTxt -eq "0.00") {$reqFailed++; "Something wrong with Price $priceTxt" | Out-File $logFile -Encoding OEM -append; continue;} # wrong last price, ignore line

            $reqSucceed++; # at this point we have proper price succeeded

            $date = (Get-Date).ToString("yyyy-MM-dd"); $dayOfWeek = (Get-Date).DayOfWeek.ToString();
            if ($dayOfWeek -eq "Saturday" -or $dayOfWeek -eq "Sunday") {$weekendFlag = $true} else {$weekendFlag=$false};

            $ret = GetCurrExchInfo $currFrom $currTo $currExchFolder $minDate; $currExchFile = $ret[0]; $nextDate = $ret[1]; $lastQuote = $ret[2];
            "   CurrExch. From: $currFrom -> To: $currTo. Next date: $nextDate. Quote file: $currExchFile" | Out-File $logFile -Encoding OEM -Append;
    
            if ($date -ge $nextDate -and (!$weekendFlag)) {
                "$date,$priceTxt,$currFrom,$currTo" | Out-File $currExchIDFile -Encoding OEM -append; $reqRowsT++; if("$date $time" -gt $lastDateTime) {$lastDateTime = "$date $time"};
                " New quote date: " + $date + " " + $time | Out-File $logFile -Encoding OEM -append; $reqUsed++;
            }
            else {" Newer quote not found" | Out-File $logFile -Encoding OEM -append; }
        }
    }
}
$duration = (NEW-TIMESPAN -Start $startTime -End (Get-Date)).TotalSeconds.ToString("#,##0") + " sec.";

(Get-Date).ToString("HH:mm:ss") + " --- Finished. CurrExch Requested/Succeed/Failed/Used: $reqCount/$reqSucceed/$reqFailed/$reqUsed. Duration: $duration. Date-Time: " + $lastDateTime + "`r`n" | Out-File $logFile -Encoding OEM -append;
$logSummary + ". CurrExch Requested/Succeed/Failed/Used: $reqCount/$reqSucceed/$reqFailed/$reqUsed. Date: " + $lastDateTime + ". Duration: $duration";
