# API URI - these are examples, only
$goldPriceApi='https://dragoncoin.com/XAU/USD'
$silverPriceApi='https://dragoncoin.com/XAG/USD'
$platinumApi='https://dragoncoin.com/XPT/USD'
$palladiumApi='https://dragoncoin.com/XPD/USD'

# WordPress WooCommerce Gold Price Plugin
$username='someadminaccount'
$password='somepassword'
$goldPriceFieldId='woocommerce_gold_price_options_24'
$agFieldId='woocommerce_gold_price_options_22'
$ptFieldId='woocommerce_gold_price_options_18'
$pdFieldId='woocommerce_gold_price_options_14'
$submitButtonClass='button-primary'
$expectedPageTitle='Gold Prices and Gold Products*'
$goldPricePage="https://dragoncoin.com/wp-admin/admin.php?page=woocommerce_gold_price"
$updateSeconds=120

# Other variables
$countDown=0
$timesToDeterminePause=5
$pauseSeconds=3600

function autologinSe{
    param(
        $url,
        $username,
        $password,
        $usernameElementId='user_login',
        $passwordElementId='user_pass',
        $submitButtonId='wp-submit',
        $exitIeWhenDone=$false
    )
    $ErrorActionPreference = 'continue'

    # Initial validation
    if(!$url){write-warning "No URL specified.";return $false}

    function killInternetExplorer{
        $ieInstances=(New-Object -COM 'Shell.Application').Windows()|?{$_.Name -like '*Internet Explorer*'} 
        $ieInstances|%{$_.Quit()
        [Runtime.Interopservices.Marshal]::ReleaseComObject($_)
        }
        [GC]::Collect()
        [GC]::WaitForPendingFinalizers()
        }

    function enableIeProtectedMode{
        # $hives = 0..4|%{"HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\$_"}
        $hives = 0..4|%{"HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\$_"}
        $keyName='2500' # Key Name '2500' corresponds to 'Protected Mode' in IE
        
        #Skipping zone 0 as that is the default local machine zone
        $hives[1..4]|%{Set-ItemProperty -Path $_ -Name $keyName -Value 0}
        $keys=$hives|%{Get-ItemProperty -Path $_}|select DisplayName, `
                                                        @{name='status';e={
                                                                        if($_.$keyName -eq 0){'enabled'}
                                                                        elseif($_.$keyName -eq 3){'disabled'}
                                                                        else{'n/a'}                                                                                        
                                                                        }}
        write-host "IE Protected Mode Standardized Values:`r`n$($keys|out-string)" 
    }

    function disableIeProtectedMode{
        # $hives = 0..4|%{"HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\$_"}
        $hives = 0..4|%{"HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\$_"}
        $keyName='2500' # Key Name '2500' corresponds to 'Protected Mode' in IE
        
        #Skipping zone 0 as that is the default local machine zone
        $hives[1..4]|%{Set-ItemProperty -Path $_ -Name $keyName -Value 3}
        $keys=$hives|%{Get-ItemProperty -Path $_}|select DisplayName, `
                                                        @{name='status';e={
                                                                        if($_.$keyName -eq 0){'enabled'}
                                                                        elseif($_.$keyName -eq 3){'disabled'}
                                                                        else{'n/a'}                                                                                        
                                                                        }}
        write-host "IE Protected Mode Standardized Values:`r`n$($keys|out-string)" 
    }

    function allowActiveX($zone='Trusted'){
        #Source: https://learn.microsoft.com/en-us/previous-versions/troubleshoot/browsers/security-privacy/ie-security-zones-registry-entries
        $zoneCode=switch($zone){
            'My Computer'{0;break}
            'Local Intranet'{1;break}
            'Trusted'{2;break}
            'Internet'{3;break}
            'Restricted Sites'{4;break}
            default{2}
            }
        #Reference table:
        #Value    Setting
        #------------------------------
        #0        My Computer
        #1        Local Intranet Zone
        #2        Trusted sites Zone
        #3        Internet Zone
        #4        Restricted Sites Zone
        $hashMap=@{
            '2702'=0 #ActiveX controls and plug-ins: Allow ActiveX Filtering = Enable (2702)
            '1208'=0 #ActiveX controls and plug-ins: Allow previously unused ActiveX controls to run without prompt = Enable (1208)
            '1209'=0 #ActiveX controls and plug-ins: Allow Scriptlets = Enable (1209)
            '2201'=3 #ActiveX controls and plug-ins: Automatic prompting for ActiveX controls = Disable (2201)
            '2000'=0 #ActiveX controls and plug-ins: Binary and script behaviors = Enable (2000)
            '120A'=0 #Display video and animation on a webpage that does not use external media player = Enable (120A)
            '1001'=0 #ActiveX controls and plug-ins: Download signed ActiveX controls = Enable (1001)
            '1004'=0 #ActiveX controls and plug-ins: Download unsigned ActiveX controls = Enable (1004)
            '1201'=0 #ActiveX controls and plug-ins: Initialize and script ActiveX controls not marked as safe for scripting = Enable (1201)
            '120B'=3 #Only allow approved domains to use ActiveX without prompt = Disable (120B)
            '1200'=0 #ActiveX controls and plug-ins: Run ActiveX controls and plug-ins = Enable (1200)
            '1405'=0 #ActiveX controls and plug-ins: Script ActiveX controls marked as safe for scripting = Enable (1405)
            }        
        
        $trustedDomains="HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\zones\$zoneCode"
        $currentValues=Get-ItemProperty $trustedDomains
        foreach ($item in $hashMap.GetEnumerator()) {
            $key = $item.Key
            $value = $item.Value
            if($currentValues.$key -ne $value){
                    New-ItemProperty -Path $trustedDomains -Name $key -Value $value -PropertyType DWORD -Force
                }
        }
    }

    function addDomainToTrustedSites($url){
        $httpType=.{[void]($url -match '^(https{0,1})');$matches[1]}
        $domain=([uri]$url).Host
        #$rootDomain=$domain.split('.')[-2..-1] -join '.' # This is assuming that the TLD is one-dotted (e.g. .com) not two-dotted (e.g. co.uk)
        $rootDomain=.{$fragments=$domain.split('.')
                    $fragments[1..$($fragments.count)] -join '.'
                    }
        write-host "Root domain detected`t: $rootDomain"        
        # The more advanced function to retrieve this value is at https://blog.kimconnect.com/powershell-extract-root-domain-from-url/
        if ($rootDomain -notmatch '\.' -or $rootDomain -eq $env:USERDNSDOMAIN){
            write-host "There's no need to add $url to the Trusted zone as it is local to this domain."
            return $true
            }
        $dwordValue=2 # value of true correlates to 'enable'
        $domainRegistryPath='HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains'
        $domainRegistryPath2='HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains' #EscDomains key applies to those protocols that are affected by the Enhanced Security Configuration (ESC)
        $null=New-Item -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap' -ItemType File -Name 'EscDomains' -Force
        $null=New-Item -Path "$domainRegistryPath" -ItemType File -Name "$rootDomain" -Force
        $null=New-Item -Path "$domainRegistryPath2" -ItemType File -Name "$rootDomain" -Force
        $null=Set-ItemProperty -Path "$domainRegistryPath\$rootDomain" -Name $httpType -Value $dwordValue
        $null=Set-ItemProperty -Path "$domainRegistryPath2\$rootDomain" -Name $httpType -Value $dwordValue

        # Also add {about:blank} record as that doesn't seem to have been added by default
        if (!(test-path "$domainRegistryPath\blank")){
            #New-ItemProperty -Path $trustedDomains -Name $key -Value $value -PropertyType DWORD -Force
            $null=New-Item -Path "$domainRegistryPath" -ItemType File -Name 'blank'
            $null=Set-ItemProperty -Path "$domainRegistryPath\blank" -Name 'about' -Value $dwordValue
            }
        if (!(test-path "$domainRegistryPath2\blank")){
            $null=New-Item -Path "$domainRegistryPath2" -ItemType File -Name 'blank'
            $null=Set-ItemProperty -Path "$domainRegistryPath2\blank" -Name 'about' -Value $dwordValue
            }                     

        # Also add {about:internet} record since it will stop a login when missing
        if (!(test-path "$domainRegistryPath\internet")){
            $null=New-Item -Path "$domainRegistryPath" -ItemType File -Name 'internet'
            $null=Set-ItemProperty -Path "$domainRegistryPath\internet" -Name 'about' -Value $dwordValue
            }
        if (!(test-path "$domainRegistryPath2\internet")){
            $null=New-Item -Path "$domainRegistryPath2" -ItemType File -Name 'internet'
            $null=Set-ItemProperty -Path "$domainRegistryPath2\internet" -Name 'about' -Value $dwordValue
            } 
            
        $valueAfterChanged=(Get-ItemProperty "$domainRegistryPath\$rootDomain")."$httpType"
        $value2AfterChanged=(Get-ItemProperty "$domainRegistryPath2\$rootDomain")."$httpType"
        if ($valueAfterChanged -eq 2 -and $value2AfterChanged -eq 2 ){
            write-host "$rootDomain has been added to Internet Explorer"
            return $true
            }
        else{
            write-warning "$rootDomain has NOT been added to Internet Explorer."
            return $false
            }
    }

    function includeSelenium{
        Import-Module Selenium -ea SilentlyContinue
        if (!(get-module selenium -EA SilentlyContinue)){
            Start-job {
                [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
                if(!(Get-PackageProvider Nuget -ea SilentlyContinue)){Install-PackageProvider -Name NuGet -Force}
                # Defining $ENV:ChocotaleyInstall so that it would be called by refreshenv
                $ENV:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.."   
                Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
                Install-Module Selenium -Force -Confirm:$False
                } |Receive-Job -Wait
            Update-SessionEnvironment
            Import-Module Selenium
            }
        }

    function invokeSelenium($url,$userName,$password,$usernameElementId,$passwordElementId,$submitButtonId){
        $ErrorActionPreference = "Stop"
        function closeSelenium($selenium){
            if($selenium){
                $selenium.close()
                $selenium.quit()
                }
            }
        
        function noLogin($url){
            $seleniumIe = New-Object "OpenQA.Selenium.IE.InternetExplorerDriver"
            $seleniumIe.Navigate().GoToURL($url)
            $title=$seleniumIe.Title
            write-host "Page reached: '$title'"
            $trustedSiteError=$title -match '^Error'
            if($trustedSiteError){
                write-host "An site trust issue has been detected. Adding root domain to the trusted sites list to resolve this issue."
                addDomainToTrustedSites $url              
                closeSelenium $seleniumIe                
                return $false
                }
            else{
                return $seleniumIe                
                }
            }

        function login($url,$username,$password,$usernameElementId,$passwordElementId,$submitButtonId){
            $seleniumIe = New-Object "OpenQA.Selenium.IE.InternetExplorerDriver"
            $seleniumIe.Navigate().GoToURL($url)
            $userField=$seleniumIe.FindElementById($usernameElementId)
            $userField.clear()
            $userField.SendKeys($username)
            $passwordField=$seleniumIe.FindElementById($passwordElementId)
            $passwordField.SendKeys('')
            $passwordField.clear()           
            $passwordField.SendKeys($password)
            $submitButton=$seleniumIe.FindElementById($submitButtonId)
            $submitButton.Click()
            $title=$seleniumIe.Title
            write-host "Page reached: '$title'"
            $trustedSiteError=$title -match '^Error'
            if($trustedSiteError){
                write-warning "A site trust issue has been detected."                
                closeSelenium $seleniumIe                
                return $false
            }else{
                return $seleniumIe                
                }
            }
        
        try{
            $null=allowActiveX
            $isLogin=$userName,$password,$usernameElementId,$passwordElementId,$submitButtonId|?{!(!$_)}
            if($isLogin){
                write-host "Login to $url as $userName..."
                $ie=login $url $userName $password $usernameElementId $passwordElementId $submitButtonId
            }else{
                write-host "Accesing $url without login..."
                $ie=nologin $url
                }
            return $ie
            }
        catch{            
            Write-Warning $Error[0].Exception.Message
            return $false
            }
        }

    try{
        write-host "Username`t: $username`r`nPassword`t: $(!(!$password))`r`nusernameElementId`t: $usernameElementId`r`npasswordElementId`t: $passwordElementId`r`nsubmitButtonId`t: $submitButtonId"
        $null=includeSelenium
        $null=disableIeProtectedMode
        $null=addDomainToTrustedSites $url                   
        if(get-module selenium -ea SilentlyContinue){
            $isLogin=$userName,$password,$usernameElementId,$passwordElementId,$submitButtonId|?{!(!$_)}
            if($isLogin){                
                $selenium=invokeSelenium $url $userName $password $usernameElementId $passwordElementId $submitButtonId
            }else{
                write-host "No username or password are given. Proceeding to access only the provided URL."
                $selenium=invokeSelenium $url
                }
        }else{
            write-warning "Please manually verify that the Selenium module is installed before retrying this function."
            }
        if($selenium){            
            if($exitIeWhenDone){
                $null=killInternetExplorer
                return $true
            }else{
                return $selenium
                }
        }else{
            write-warning "There were errors preventing a successful login."
            return $false
            }
        }
    catch {
        write-warning "$_"        
        return $false
        }
    
    # Note on a common error:
    #New-Object : Exception calling ".ctor" with "0" argument(s): "Unexpected error launching Internet Explorer. Protected
    #Mode settings are not the same for all zones. Enable Protected Mode must be set to the same value (enabled or
    #disabled) for all zones. (SessionNotCreated)"
    #At line:1 char:15
    #+ $seleniumIe = New-Object "OpenQA.Selenium.IE.InternetExplorerDriver"
    #+               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    #    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
    # Solution: either DISABLE or ENABLE Protected mode for ALL ZONES
}

function getMetalPrice{
    param(
        $metalName='gold',
        $metalPriceApi
    )
    $priceData=Invoke-RestMethod $metalPriceApi
    $price=[math]::round(($priceData.spreadProfilePrices.ask | Measure-Object -Average).Average,2)
    write-host "$(get-date) --- $metalName price is $price"
    return [hashtable]@{
        $metalName=$price
    }
}

function updateFieldValues{
    param(
        $ie,
        $fieldId,
        $value
    )
    $field=$ie.FindElementById($fieldId)
    $field.clear()
    $field.SendKeys($value)
}

function updateMetalPrices{
    Do{
        if($countDown -lt $timesToDeterminePause){
            $changesDetected=$false
            $prices=$null
            $title=$ie.Title
            if($title -notlike $expectedPageTitle){
                $null=get-process iexplore | stop-process -force
                $ie=autoLoginSe $goldPricePage $username $password
                if($ie.Title -ne $expectedPageTitle){$ie.Navigate().GoToURL($goldPricePage)}
                clear-Host
                write-host "Connected to $goldPricePage"
            }
            $goldPrice=getMetalPrice 'gold' $goldPriceApi
            $silverPrice=getMetalPrice 'silver' $silverPriceApi
            $platinumPrice=getMetalPrice 'platinum' $platinumApi
            $palladiumPrice=getMetalPrice 'palladium' $palladiumApi
            $prices=$goldPrice+$silverPrice+$platinumPrice+$palladiumPrice
            if(!$goldPrice){
                write-warning "Cannot update if gold price value is Null"
            }elseif($previousAuPrice -ne $goldPrice){
                updateFieldValues $ie $goldPriceFieldId $prices.gold
                $changesDetected=$true
            }else{
                write-host "Gold prices haven't changed."
            }
            if(!$silverPrice){
                write-warning "Cannot update if silver price value is Null"
            }elseif($previousAgPrice -ne $silverPrice){
                updateFieldValues $ie $agFieldId $prices.silver
                $changesDetected=$true
            }else{
                write-host "Silver prices haven't changed."
            }
            if(!$platinumPrice){
                write-warning "Cannot update if gold price value is Null"
            }elseif($previousPtPrice -ne $platinumPrice){
                updateFieldValues $ie $ptFieldId $prices.platinum
                $changesDetected=$true
            }else{
                write-host "Platinum prices haven't changed."
            }
            if(!$palladiumPrice){
                write-warning "Cannot update if gold price value is Null"
            }elseif($previousPdPrice -ne $palladiumPrice){
                updateFieldValues $ie $pdFieldId $prices.palladium
                $changesDetected=$true
            }else{
                write-host "Palladium prices haven't changed."
            }
            if($changesDetected){
                $submitButton=$ie.findElementByClassName($submitButtonClass)
                $submitButton.Click()
                $countDown=0
            }else{
                $countDown++
            }
            $previousAuPrice=$goldPrice
            $previousAgPrice=$silverPrice
            $previousPtPrice=$platinumPrice
            $previousPdPrice=$palladiumPrice
            write-host "time-out $updateSeconds seconds until next iteration"
            Start-Sleep -s $updateSeconds
        }else{
            Start-Sleep -s $pauseSeconds
            $countDown=0
        }
    }until($false)
}

updateMetalPrices