From 9feab3fe2caa883efc20c3309cf358097c1fddf3 Mon Sep 17 00:00:00 2001
From: MrPlusGH <36303784+MrPlusGH@users.noreply.github.com>
Date: Tue, 19 May 2020 14:39:44 +0200
Subject: [PATCH] 7.4.2
---
.gitattributes | 1 +
.gitignore | 4 +-
Config/config.json | 22 +-
Includes/Algorithms.txt | 6 +-
Includes/Charting.ps1 | 38 +-
Includes/Core.ps1 | 596 ++++++++++--------
Includes/EarningsTracker.ps1 | 301 +++++++++
Includes/EarningsTrackerJob.ps1 | 64 +-
Includes/Include.ps1 | 387 +++++++++---
Includes/MinerCustomConfig.ps1 | 51 +-
Includes/MinerCustomConfigEditor.ps1 | 4 +-
Includes/Server.ps1 | 872 +++++++++++++++++++++++++++
Includes/Web.css | 93 +++
Logs/CoinIcons.json | Bin 0 -> 42818 bytes
NPlusMiner.ps1 | 344 +++++++++--
Pools/ahashpool.ps1 | 51 ++
Pools/ahashpool24hr.ps1 | 51 ++
Pools/ahashpoolplus.ps1 | 57 ++
Pools/blazepool.ps1 | 51 ++
Pools/blazepool24hr.ps1 | 51 ++
Pools/blazepoolplus.ps1 | 57 ++
Pools/blockmasters.ps1 | 61 ++
Pools/blockmasters24hr.ps1 | 62 ++
Pools/blockmastersplus.ps1 | 70 +++
Pools/miningpoolhub.ps1 | 65 ++
Pools/nicehash.ps1 | 79 +++
Pools/nlpool.ps1 | 57 ++
Pools/nlpool24hr.ps1 | 57 ++
Pools/nlpoolplus.ps1 | 62 ++
Pools/prohashing.ps1 | 59 ++
Pools/prohashing24hr.ps1 | 59 ++
Pools/prohashingplus.ps1 | 66 ++
Pools/zergpool.ps1 | 51 ++
Pools/zergpool24hr.ps1 | 51 ++
Pools/zergpoolplus.ps1 | 59 ++
Pools/zpool.ps1 | 32 +-
Pools/zpool24hr.ps1 | 28 +-
Pools/zpoolplus.ps1 | 90 +--
README.md | 252 +++++++-
Utils/API-Mine.ps1 | 14 +
Utils/API-Pause.ps1 | 14 +
Utils/WebUI.png | Bin 0 -> 89875 bytes
version.json | 2 +-
43 files changed, 3876 insertions(+), 515 deletions(-)
create mode 100644 .gitattributes
create mode 100644 Includes/EarningsTracker.ps1
create mode 100644 Includes/Server.ps1
create mode 100644 Includes/Web.css
create mode 100644 Logs/CoinIcons.json
create mode 100644 Pools/ahashpool.ps1
create mode 100644 Pools/ahashpool24hr.ps1
create mode 100644 Pools/ahashpoolplus.ps1
create mode 100644 Pools/blazepool.ps1
create mode 100644 Pools/blazepool24hr.ps1
create mode 100644 Pools/blazepoolplus.ps1
create mode 100644 Pools/blockmasters.ps1
create mode 100644 Pools/blockmasters24hr.ps1
create mode 100644 Pools/blockmastersplus.ps1
create mode 100644 Pools/miningpoolhub.ps1
create mode 100644 Pools/nicehash.ps1
create mode 100644 Pools/nlpool.ps1
create mode 100644 Pools/nlpool24hr.ps1
create mode 100644 Pools/nlpoolplus.ps1
create mode 100644 Pools/prohashing.ps1
create mode 100644 Pools/prohashing24hr.ps1
create mode 100644 Pools/prohashingplus.ps1
create mode 100644 Pools/zergpool.ps1
create mode 100644 Pools/zergpool24hr.ps1
create mode 100644 Pools/zergpoolplus.ps1
create mode 100644 Utils/API-Mine.ps1
create mode 100644 Utils/API-Pause.ps1
create mode 100644 Utils/WebUI.png
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..efd645d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+Includes/NPM.gif export-ignore
diff --git a/.gitignore b/.gitignore
index e83e7c2..f7a0f94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
Bin/*
Stats/*
-Logs/*
*.rar
*.7z
-*.zip
\ No newline at end of file
+*.zip
+Includes/NPM.gif
diff --git a/Config/config.json b/Config/config.json
index 0d1f56b..ac9c3fc 100644
--- a/Config/config.json
+++ b/Config/config.json
@@ -11,13 +11,13 @@
"Currency": "USD",
"Delay": 1,
"DisableGPU0": false,
- "Donate": 8,
+ "Donate": 16,
"EnableEarningsTrackerLogs": false,
"FirstInterval": 30,
"HideConsole": true,
"IdleSec": "15",
"IncludeOptionalMiners": false,
- "Interval": 240,
+ "Interval": 300,
"IsFixedSize": false,
"IsReadOnly": false,
"IsSynchronized": true,
@@ -37,20 +37,30 @@
"Passwordcurrency": "BTC",
"PenalizeSoloInPlus": true,
"PoolName": [
- "nicehash",
- "miningpoolhub",
- "zergpoolplus"
+ "zpoolplus"
],
"Proxy": "",
"ReportToServer": false,
"SelGPUCC": "0",
"SelGPUDSTM": "0",
+ "Server_Client": true,
+ "Server_ClientIP": "127.0.0.1",
+ "Server_ClientPassword": "3890292d-990c-44ba-b779-552829fc0bc4",
+ "Server_ClientPort": "4028",
+ "Server_ClientUser": "NPlusMiner",
+ "Server_Log": false,
+ "Server_On": true,
+ "Server_Password": "3890292d-990c-44ba-b779-552829fc0bc4",
+ "Server_Port": "4028",
+ "Server_ServerProxyTimeOut": 1,
+ "Server_Standalone": false,
+ "Server_User": "NPlusMiner",
"ShowOnlyTopCoins": true,
"ShowWorkerStatus": false,
"SSL": false,
"StartGUIMinimized": false,
"StartPaused": false,
- "StatsInterval": 180,
+ "StatsInterval": 60,
"SyncRoot": {
},
diff --git a/Includes/Algorithms.txt b/Includes/Algorithms.txt
index 3b5afa4..d977080 100644
--- a/Includes/Algorithms.txt
+++ b/Includes/Algorithms.txt
@@ -110,10 +110,12 @@
"equihash": "Equihash",
"exosis": "Exosis",
"equihash192": "Equihash192",
+ "EquihashZcl": "Equihash192",
+ "Equihash1254": "Equihash125",
"equihash144": "Equihash144",
+ "Equihash1445": "Equihash144",
+ "EquihashBTG": "Equihash144",
"equihash96": "Equihash96",
- "equihash-btg": "EquihashBTG",
- "equihashbtg": "EquihashBTG",
"Espers": "HMQ1725",
"eth": "Ethash",
"ethash": "Ethash",
diff --git a/Includes/Charting.ps1 b/Includes/Charting.ps1
index 9f72a8a..b7cd667 100644
--- a/Includes/Charting.ps1
+++ b/Includes/Charting.ps1
@@ -30,7 +30,9 @@ param(
[Parameter(Mandatory=$true)]
[String]$Width = 700,
[Parameter(Mandatory = $true)]
- [String]$Height = 85
+ [String]$Height = 85,
+ [Parameter(Mandatory = $false)]
+ [String]$Currency = "BTC"
)
Function GetNextColor {
@@ -115,8 +117,11 @@ Switch ($Chart) {
# $BaseColor = "424B54"
$BaseColor = "FFFFFF"
# $BaseColor = "F7931A"
- $Color = $BaseColor
- $A=255
+ $StartColor = "FFFFFF"
+ $EndColor = "f2a900"
+ $i=0
+ # $Colors = Get-ColorPalette $StartColor $EndColor (($datasource | sort DaySum -Unique).DaySum | % {[math]::Round($_*1000, 3)} | sort -Unique).count
+ $Colors = Get-ColorPalette $StartColor $EndColor 100
[void]$chart1.Series.Add("Total")
$chart1.Series["Total"].ChartType = "Column"
@@ -129,11 +134,17 @@ Switch ($Chart) {
# $chart1.Series[$Pool].color = [System.Drawing.Color]::FromArgb($A,247,147,26)
$chart1.Series["Total"].label = "#VALY{N3}"
$chart1.Series["Total"].LabelForeColor = "#FFFFFF"
- $chart1.Series["Total"].ToolTip = "#VALX: #VALY mBTC" # - Total: #TOTAL mBTC";
+ $chart1.Series["Total"].ToolTip = "#VALX: #VALY" # - Total: #TOTAL mBTC";
# $datasource | select Date,DaySum -Unique | ForEach-Object {$chart1.Series["Total"].Points.addxy( $_.Date , ("{0:N3}" -f ([Decimal]$_.DaySUm*1000))) | Out-Null }
- $datasource | select Date,DaySum -Unique | ForEach-Object {$chart1.Series["Total"].Points.addxy( $_.Date , (([Decimal]$_.DaySUm*1000))) | Out-Null }
+ $datasource | select Date,DaySum -Unique | ForEach-Object {$chart1.Series["Total"].Points.addxy( $_.Date , (([Decimal](Get-DisplayCurrency $_.DaySum).Value))) | Out-Null }
$Chart1.Series | foreach {$_.CustomProperties = "DrawSideBySide=True"}
+ Try{
+ $Chart1.Series["Total"].Points | ForEach-Object {
+ # $PSItem.Color = "#$($Colors[((($datasource | sort DaySum -Unique).DaySum | % {[math]::Round($_*1000, 3)} | sort -Unique)).IndexOf([math]::Round(($PSItem.YValues[0]), 3))])"
+ $PSItem.Color = "#$($Colors[[Int](100 * ($PSItem.YValues[0]) / (($datasource | group date | % {($_.group.DailyEarnings | measure -sum).sum} | measure -maximum).maximum * 1000))])"
+ }
+ } Catch {}
}
"Front7DaysEarningsWithPoolSplit" {
$datasource = If (Test-Path ".\logs\DailyEarnings.csv" ) {Import-Csv ".\logs\DailyEarnings.csv" | ? {[DateTime]$_.date -ge (Get-Date).AddDays(-7)}}
@@ -186,8 +197,8 @@ Switch ($Chart) {
$chart1.Series[$Pool].color = "#$($Color)"
# $chart1.Series[$Pool].color = [System.Drawing.Color]::FromArgb($A,247,147,26)
# $chart1.Series[$Pool].label = "#VALY"
- $chart1.Series[$Pool].ToolTip = "#SERIESNAME: #VALY mBTC" # - Total: #TOTAL mBTC";
- $datasource | ? {$_.Pool -eq $Pool} | ForEach-Object {$chart1.Series[$Pool].Points.addxy( $_.Date , ("{0:N3}" -f ([Decimal]$_.DailyEarnings*1000))) | Out-Null }
+ $chart1.Series[$Pool].ToolTip = "#SERIESNAME: #VALY" # - Total: #TOTAL mBTC";
+ $datasource | ? {$_.Pool -eq $Pool} | ForEach-Object {$chart1.Series[$Pool].Points.addxy( $_.Date , ("{0:N3}" -f (([Decimal](Get-DisplayCurrency $_.DailyEarnings).Value)))) | Out-Null }
}
[void]$chart1.Series.Add("Total")
@@ -200,13 +211,14 @@ Switch ($Chart) {
$chart1.Series["Total"].color = "#FFFFFF"
# $chart1.Series[$Pool].color = [System.Drawing.Color]::FromArgb($A,247,147,26)
# $chart1.Series[$Pool].label = "#VALY"
- $chart1.Series["Total"].ToolTip = "#SERIESNAME: #VALY mBTC" # - Total: #TOTAL mBTC";
- $datasource | select Date,DaySum -Unique | ForEach-Object {$chart1.Series[$Pool].Points.addxy( $_.Date , ("{0:N3}" -f ([Decimal]$_.DaySUm*1000))) | Out-Null }
+ $chart1.Series["Total"].ToolTip = "#SERIESNAME: #VALY" # - Total: #TOTAL mBTC";
+ $datasource | select Date,DaySum -Unique | ForEach-Object {$chart1.Series[$Pool].Points.addxy( $_.Date , ("{0:N3}" -f (([Decimal](Get-DisplayCurrency $_.DailyEarnings).Value)))) | Out-Null }
$Chart1.Series | foreach {$_.CustomProperties = "DrawSideBySide=True"}
}
"DayPoolSplit" {
- $datasource = If (Test-Path ".\logs\DailyEarnings.csv" ) {Import-Csv ".\logs\DailyEarnings.csv" | ? {$_.date -ge (Get-Date).date.AddDays(-1).ToString("MM/dd/yyyy")}}
+ $datasource = If (Test-Path ".\logs\DailyEarnings.csv" ) {Import-Csv ".\logs\DailyEarnings.csv" | ? {[datetime]$_.date -ge [datetime](Get-Date).date.AddDays(-1).ToString("MM/dd/yyyy")}}
+ # $datasource = If (Test-Path ".\logs\DailyEarnings.csv" ) {Import-Csv ".\logs\DailyEarnings.csv" }
$dataSource | % {$_.DailyEarnings = [Decimal]$_.DailyEarnings}
$datasource = $dataSource | ? {$_.DailyEarnings -gt 0} | sort DailyEarnings #-Descending
@@ -263,13 +275,13 @@ Switch ($Chart) {
# $chart1.Series[$Pool].color = "#FFFFFF"
# $chart1.Series[$Pool].color = [System.Drawing.Color]::FromArgb($A,247,147,26)
# $chart1.Series[$Pool].label = "#SERIESNAME: #VALY mBTC"
- $chart1.Series[$Pool].ToolTip = "#VALX - #SERIESNAME: #VALY mBTC" # - Total: #TOTAL mBTC";
- $datasource | ? {$_.Pool -eq $Pool} | Sort date | ForEach-Object {$chart1.Series[$Pool].Points.addxy( $_.Date , ("{0:N3}" -f ([Decimal]$_.DailyEarnings*1000))) | Out-Null }
+ $chart1.Series[$Pool].ToolTip = "#VALX - #SERIESNAME: #VALY" # - Total: #TOTAL mBTC";
+ $datasource | ? {$_.Pool -eq $Pool} | Sort date | ForEach-Object {$chart1.Series[$Pool].Points.addxy( $_.Date , ("{0:N3}" -f (([Decimal](Get-DisplayCurrency $_.DailyEarnings).Value)))) | Out-Null }
# $Chart1.Series["Data"].Points.DataBindXY($datasource.pool, $datasource.DailyEarnings)
}
}
}
# save chart
- # $chart1.SaveImage("$scriptpath\ChartTest.png","png") | Out-Null
+ $chart1.SaveImage(".\Logs\$($chart).png","png") | Out-Null
$chart1
diff --git a/Includes/Core.ps1 b/Includes/Core.ps1
index f38b656..5f36b75 100644
--- a/Includes/Core.ps1
+++ b/Includes/Core.ps1
@@ -25,13 +25,21 @@ version date: 20191110
#>
Function InitApplication {
- $Variables | Add-Member -Force @{SourcesHash = @()}
- $Variables | Add-Member -Force @{ProcessorCount = (Get-WmiObject -class win32_processor).NumberOfLogicalProcessors}
-
+ $Variables.SourcesHash = @()
+ $Variables.ProcessorCount = (Get-WmiObject -class win32_processor).NumberOfLogicalProcessors
+
+ $ServerPasswd = ConvertTo-SecureString $Config.Server_Password -AsPlainText -Force
+ $ServerCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_User, $ServerPasswd)
+ $Variables.ServerCreds = $ServerCreds
+ $ServerClientPasswd = ConvertTo-SecureString $Config.Server_ClientPassword -AsPlainText -Force
+ $ServerClientCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_ClientUser, $ServerClientPasswd)
+ $Variables.ServerClientCreds = $ServerClientCreds
+
if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+ if (!(IsLoaded(".\Includes\Server.ps1"))) {. .\Includes\Server.ps1;RegisterLoaded(".\Includes\Server.ps1")}
Set-Location (Split-Path $script:MyInvocation.MyCommand.Path)
- $Variables | Add-Member -Force @{ScriptStartDate = (Get-Date)}
+ $Variables.ScriptStartDate = (Get-Date)
# GitHub Supporting only TLSv1.2 on feb 22 2018
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
Set-Location (Split-Path $script:MyInvocation.MyCommand.Path)
@@ -45,11 +53,13 @@ Function InitApplication {
if($Proxy -eq ""){$PSDefaultParameterValues.Remove("*:Proxy")}
else{$PSDefaultParameterValues["*:Proxy"] = $Proxy}
Update-Status("Initializing Variables...")
- $Variables | Add-Member -Force @{DecayStart = Get-Date}
- $Variables | Add-Member -Force @{DecayPeriod = 120} #seconds
- $Variables | Add-Member -Force @{DecayBase = 1-0.1} #decimal percentage
- $Variables | Add-Member -Force @{ActiveMinerPrograms = @()}
- $Variables | Add-Member -Force @{Miners = @()}
+ $Variables.DecayStart = Get-Date
+ $Variables.DecayPeriod = 120 #seconds
+ $Variables.DecayBase = 1-0.1 #decimal percentage
+ # $Variables | Add-Member -Force @{ActiveMinerPrograms = @()}
+ $Variables["ActiveMinerPrograms"] = [System.Collections.ArrayList]::Synchronized(@())
+ # $Variables | Add-Member -Force @{Miners = @()}
+ # $Variables["Miners"] = [System.Collections.ArrayList]::Synchronized(@())
#Start the log
Start-Transcript -Path ".\Logs\miner-$((Get-Date).ToString('yyyyMMdd')).log" -Append -Force
# Purge Logs more than 10 days
@@ -59,22 +69,22 @@ Function InitApplication {
#Update stats with missing data and set to today's date/time
if(Test-Path "Stats"){Get-ChildItemContent "Stats" | ForEach {$Stat = Set-Stat $_.Name $_.Content.Week}}
#Set donation parameters
- $Variables | Add-Member -Force @{DonateRandom = [PSCustomObject]@{}}
- $Variables | Add-Member -Force @{LastDonated = (Get-Date).AddDays(-1).AddHours(1)}
+ # $Variables | Add-Member -Force @{DonateRandom = [PSCustomObject]@{}}
+ $Variables.LastDonated = (Get-Date).AddDays(-1).AddHours(1)
# If ($Config.Donate -lt 3) {$Config.Donate = (0,(3..8)) | Get-Random}
- $Variables | Add-Member -Force @{WalletBackup = $Config.Wallet}
- $Variables | Add-Member -Force @{UserNameBackup = $Config.UserName}
- $Variables | Add-Member -Force @{WorkerNameBackup = $Config.WorkerName}
- $Variables | Add-Member -Force @{EarningsPool = ""}
- $Variables | Add-Member -Force @{BrainJobs = @()}
- $Variables | Add-Member -Force @{EarningsTrackerJobs = @()}
- $Variables | Add-Member -Force @{Earnings = @{}}
-
-
- $Global:Variables | Add-Member -Force @{StartPaused = $False}
- $Global:Variables | Add-Member -Force @{Started = $False}
- $Global:Variables | Add-Member -Force @{Paused = $False}
- $Global:Variables | Add-Member -Force @{RestartCycle = $False}
+ # $Variables | Add-Member -Force @{WalletBackup = $Config.Wallet}
+ # $Variables | Add-Member -Force @{UserNameBackup = $Config.UserName}
+ # $Variables | Add-Member -Force @{WorkerNameBackup = $Config.WorkerName}
+ # $Variables | Add-Member -Force @{EarningsPool = ""}
+ $Variables.BrainJobs = @()
+ $Variables.EarningsTrackerJobs = @()
+ $Variables.Earnings = [hashtable]::Synchronized(@{})
+
+
+ $Global:Variables.StartPaused = $False
+ $Global:Variables.Started = $False
+ $Global:Variables.Paused = $False
+ $Global:Variables.RestartCycle = $False
$Location = $Config.Location
@@ -84,25 +94,50 @@ Function InitApplication {
$Config.Type | sort | foreach {
Update-Status("Finding available TCP Port for $($_)")
$Port = Get-FreeTcpPort($StartPort)
- $Variables | Add-Member -Force @{"$($_)MinerAPITCPPort" = $Port}
+ $Variables."$($_)MinerAPITCPPort" = $Port
Update-Status("Miners API Port: $($Port)")
$StartPort = $Port+1
}
Sleep 2
+ # Register Rig on Server
+ # need to decide on solution for IP address... Should be in config has could be external IP...
+ # If ($Config.Server_Client) {
+ # (Invoke-WebRequest "http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/RegisterRig/?name=$($Config.WorkerName)&IP=192.168.0.30&port=$($Config.ServerPort)" -Credential $Variables.ServerClientCreds)
+ # }
+
# Copy nvml.dll to proper location as latest drivers miss it
- If ( (! (Test-Path "C:\Program Files\NVIDIA Corporation\NVSMI\nvml.dll")) -and (Test-Path "c:\Windows\System32\nvml.dll") ) {
- Copy-Item "c:\Windows\System32\nvml.dll" "C:\Program Files\NVIDIA Corporation\NVSMI\nvml.dll" -Force -ErrorAction Ignore
- }
+ # If ( (! (Test-Path "C:\Program Files\NVIDIA Corporation\NVSMI\nvml.dll")) -and (Test-Path "c:\Windows\System32\nvml.dll") ) {
+ # Copy-Item "c:\Windows\System32\nvml.dll" "C:\Program Files\NVIDIA Corporation\NVSMI\nvml.dll" -Force -ErrorAction Ignore
+ # }
}
Function Start-ChildJobs {
+ # Stop Server on code updates
+ If ($Config.Server_On -and $Variables.LocalServerRunning -and -not (IsLoaded(".\Includes\Server.ps1"))) {
+ $Variables.StatusText = "Stopping server for code update."
+ Invoke-WebRequest "http://localhost:$($Config.Server_Port)/StopServer" -Credential $Variables.ServerCreds
+ $Variables.LocalServerRunning = $False
+ }
+
+ If ($Variables.LocalServerRunning) {$StartServerAttempt = 0}
+
+ # Starts Server if necessary
+ If ($Config.Server_On -and -not $Variables.LocalServerRunning -and $StartServerAttempt -le 5 ) {
+ . .\Includes\Server.ps1;RegisterLoaded(".\Includes\Server.ps1")
+ $Variables.StatusText = "Starting Server"
+ $Variables.StopServer = $False
+ Start-Server
+ $StartServerAttempt++
+ $Variables.LocalServerRunning = Try { ((Invoke-WebRequest "http://localhost:$($Config.Server_Port)/ping" -Credential $Variables.ServerCreds).content -eq "Server Alive")} Catch {$False}
+ }
+
# Starts Brains if necessary
$Config.PoolName | foreach { if ($_ -notin $Variables.BrainJobs.PoolName){
$BrainPath = "$($Variables.MainPath)\BrainPlus\$($_)"
$BrainName = (".\BrainPlus\"+$_+"\BrainPlus.ps1")
if (Test-Path $BrainName){
- $Variables.StatusText = "Starting BrainPlus for $($_)..."
+ $Variables.StatusText = "Starting BrainPlus for $($_)"
$BrainJob = Start-Job -FilePath $BrainName -ArgumentList @($BrainPath)
$BrainJob | Add-Member -Force @{PoolName = $_}
$Variables.BrainJobs += $BrainJob
@@ -134,16 +169,38 @@ Function Start-ChildJobs {
}
Function NPMCycle {
-$CycleTime = Measure-Command -Expression {
+# $CycleTime = Measure-Command -Expression {
+$CycleScriptBlock = {
+ [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1");"LoadedInclude" | out-host}
-
- $Variables | Add-Member -Force @{EndLoop = $False}
+
+ (Get-Date).ToString() | out-host
+
+ $Variables.EndLoop = $False
$Variables.StatusText = "Starting Cycle"
+ If ($Config.Server_Client) {
+ $Variables.ServerRunning = Try{ ((Invoke-WebRequest "http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/ping" -Credential $Variables.ServerClientCreds -TimeoutSec 3).content -eq "Server Alive")} Catch {$False}
+ If ($Variables.ServerRunning){
+ If ($Config.Server_ClientIP -and $Config.Server_ClientPort) {
+ Try {
+ Invoke-WebRequest "http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/RegisterRig/?Name=$($Config.WorkerName)&Port=$($Config.Server_Port)" -Credential $Variables.ServerClientCreds -TimeoutSec 5
+ } Catch {"INFO: Failed to register on http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/RegisterRig/?Name=$($Config.WorkerName)&Port=$($Config.Server_Port)" | out-host}
+ }
+ Try {
+ $PeersFromServer = Invoke-WebRequest "http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/Peers.json" -Credential $Variables.ServerClientCreds | convertfrom-json
+ $PeersFromServer | ? {$_.Name -ne $Config.WorkerName} | ForEach {
+ $RegServer = $_.IP
+ $RegPort = $_.Port
+ Invoke-WebRequest "http://$($RegServer):$($RegPort)/RegisterRig/?Name=$($Config.WorkerName)&Port=$($Config.Server_Port)" -Credential $Variables.ServerClientCreds -TimeoutSec 5
+ }
+ } Catch {"INFO: Failed to register on http://$($RegServer):$($RegPort)/RegisterRig/?Name=$($Config.WorkerName)&Port=$($Config.Server_Port)" | out-host}
+ }
+ }
$DecayExponent = [int](((Get-Date)-$Variables.DecayStart).TotalSeconds/$Variables.DecayPeriod)
# Ensure we get the hashrate for running miners prior looking for best miner
- $Variables.ActiveMinerPrograms | ForEach {
+ $Variables["ActiveMinerPrograms"] | ForEach {
if($_.Process -eq $null -or $_.Process.HasExited)
{
if($_.Status -eq "Running"){$_.Status = "Failed";$_.FailedCount++}
@@ -171,14 +228,17 @@ $CycleTime = Measure-Command -Expression {
}
}
}
-
+
#Activate or deactivate donation
- if((Get-Date).AddDays(-1).AddMinutes($Config.Donate) -ge $Variables.LastDonated -and $Variables.DonateRandom.wallet -eq $Null){
+ # if((Get-Date).AddDays(-1).AddMinutes($Config.Donate) -ge $Variables.LastDonated -and $Variables.DonateRandom.wallet -eq $Null){
+# $Variables.LastDonated = (Get-Date).AddDays(-1).AddMinutes(-1)
+ if((Get-Date).AddDays(-1) -ge $Variables.LastDonated -and $Variables.DonateRandom.wallet -eq $Null){
# Get donation addresses randomly from agreed developers list
# This will fairly distribute donations to Developers
# Developers list and wallets is publicly available at: http://tiny.cc/r355qy
$Variables.StatusText = "ENTERING DONATION"
- $Variables | Add-Member -Force @{ DonationRunning = $True }
+ $Variables.DonationStart = $True
+ $Variables.DonationRunning = $False
$Config.PartyWhenAvailable = $False
try {$Donation = Invoke-WebRequest "http://tiny.cc/r355qy" -TimeoutSec 15 -UseBasicParsing -Headers @{"Cache-Control"="no-cache"} | ConvertFrom-Json} catch {$Donation = @([PSCustomObject]@{Name = "mrplus";Wallet = "134bw4oTorEJUUVFhokDQDfNqTs7rBMNYy";UserName = "mrplus"},[PSCustomObject]@{Name = "nemo";Wallet = "1QGADhdMRpp9Pk5u5zG1TrHKRrdK5R81TE";UserName = "nemo"})}
if ($Donation -ne $null) {
@@ -186,6 +246,8 @@ $CycleTime = Measure-Command -Expression {
$Variables.DonateRandom = $Donation | Get-Random
$DevPoolsConfig = [PSCustomObject]@{default = [PSCustomObject]@{Wallet = $Variables.DonateRandom.Wallet;UserName = $Variables.DonateRandom.UserName;WorkerName = "$($Variables.CurrentProduct)$($Variables.CurrentVersion.ToString().replace('.',''))";PricePenaltyFactor=1}}
$Config | Add-Member -Force @{PoolsConfig = Merge-PoolsConfig -Main $Config.PoolsConfig -Secondary $DevPoolsConfig}
+ If ($Variables.DonateRandom.Donate) {$Config.Donate = $Variables.DonateRandom.Donate}
+ $Variables.DonationEndTime = (Get-Date).AddMinutes($Config.Donate)
If ($Variables.DonateRandom.PoolsConfURL) {
# Get Dev Pools Config
try {
@@ -204,9 +266,10 @@ $CycleTime = Measure-Command -Expression {
}
}
}
- if(((Get-Date).AddDays(-1) -ge $Variables.LastDonated -and $Variables.DonateRandom.Wallet -ne $Null) -or (! $Config.PoolsConfig)){
- If ($Variables.DonationRunning) {$Variables.StatusText = "EXITING DONATION"}
- $Variables | Add-Member -Force @{ DonationRunning = $False }
+ if(((Get-Date) -ge $Variables.DonationEndTime -and $Variables.DonateRandom.Wallet -ne $Null) -or (! $Config.PoolsConfig)){
+ If ($Variables.DonationStart -or $Variables.DonationRunning) {$Variables.StatusText = "EXITING DONATION"}
+ $Variables.DonationStart = $False
+ $Variables.DonationRunning = $False
$ConfigLoad = Get-Content $Config.ConfigFile | ConvertFrom-json
$ConfigLoad | % {$_.psobject.properties | sort Name | % {$Config | Add-Member -Force @{$_.Name = $_.Value}}}
$Config | Add-Member -Force -MemberType ScriptProperty -Name "PoolsConfig" -Value {
@@ -226,9 +289,10 @@ $CycleTime = Measure-Command -Expression {
}
$Variables.StatusText = "Loading BTC rate from 'api.coinbase.com'.."
Try{
- $Rates = Invoke-RestMethod "https://api.coinbase.com/v2/exchange-rates?currency=BTC" -TimeoutSec 15 -UseBasicParsing | Select-Object -ExpandProperty data | Select-Object -ExpandProperty rates
- $Config.Currency | Where-Object {$Rates.$_} | ForEach-Object {$Rates | Add-Member $_ ([Double]$Rates.$_) -Force}
- $Variables | Add-Member -Force @{Rates = $Rates}
+ # $Rates = Invoke-RestMethod "https://api.coinbase.com/v2/exchange-rates?currency=BTC" -TimeoutSec 15 -UseBasicParsing | Select-Object -ExpandProperty data | Select-Object -ExpandProperty rates
+ $Rates = Invoke-ProxiedWebRequest "https://api.coinbase.com/v2/exchange-rates?currency=$($Config.Passwordcurrency)" -TimeoutSec 15 -UseBasicParsing | convertfrom-json | Select-Object -ExpandProperty data | Select-Object -ExpandProperty rates
+ $Config.Currency.Where( {$Rates.$_} ) | ForEach-Object {$Rates | Add-Member $_ ([Double]$Rates.$_) -Force}
+ $Variables.Rates = $Rates
} catch {$Variables.StatusText = "Minor error - Failed to load BTC rate.."}
#Load the Stats
$Stats = [PSCustomObject]@{}
@@ -238,31 +302,41 @@ $CycleTime = Measure-Command -Expression {
$PoolFilter = @()
$Config.PoolName | foreach {$PoolFilter+=($_+=".*")}
Do {
- $AllPools = if(Test-Path "Pools"){Get-ChildItemContent "Pools" -Include $PoolFilter | ForEach {$_.Content | Add-Member @{Name = $_.Name} -PassThru} |
- Where {$_.SSL -EQ $Config.SSL -and ($Config.PoolName.Count -eq 0 -or ($_.Name -in $Config.PoolName)) -and (!$Config.Algorithm -or ((!($Config.Algorithm | ? {$_ -like "+*"}) -or $_.Algorithm -in ($Config.Algorithm | ? {$_ -like "+*"}).Replace("+","")) -and (!($Config.Algorithm | ? {$_ -like "-*"}) -or $_.Algorithm -notin ($Config.Algorithm | ? {$_ -like "-*"}).Replace("-",""))) )}
+ $Tries++
+ $AllPools = if(Test-Path "Pools"){[System.Collections.ArrayList]::Synchronized(@(Get-SubScriptContent "Pools" -Include $PoolFilter)).Where({$_.Content -ne $Null}) | ForEach {$_.Content | Add-Member @{Name = $_.Name} -PassThru} |
+ Where {
+ $_.SSL -EQ $Config.SSL -and ($Config.PoolName.Count -eq 0 -or ($_.Name -in $Config.PoolName)) -and (!$Config.Algorithm -or ((!($Config.AlgoInclude) -or $_.Algorithm -in $Config.AlgoInclude) -and (!($Config.AlgoExclude) -or $_.Algorithm -notin $Config.AlgoExclude)))
+ }
}
if ($AllPools.Count -eq 0) {
- $Variables.StatusText = "Waiting for pool data. retrying in 30 seconds.."
- Sleep 30
- }
- } While ($AllPools.Count -eq 0)
+ $Variables.StatusText = "Waiting for pool data. retrying in 30 seconds.."
+ Sleep 30
+ }
+ } While ($AllPools.Count -eq 0 -and $Tries -le 3)
+ $Tries = 0
$Variables.StatusText = "Computing pool stats.."
# Use location as preference and not the only one
- $LocPools = @($AllPools | ?{$_.location -eq $Config.Location})
- $AllPools = $LocPools + ($AllPools | ? {$_.name -notin $LocPools.name})
- rv LocPools
+ $AllPoolsTemp = $AllPools
+ "AllPoolsCount = $($AllPools.Count)" | out-host
+ $AllPools = @($AllPools.Where({$_.location -eq $Config.Location}))
+ "AllPoolsCount = $($AllPools.Count) Loc only" | out-host
+ # $AllPools = $AllPools + @($AllPoolsTemp.Where({$_.name -notin $AllPools.name}))
+ $AllPools += (($AllPoolsTemp | sort name,algorithm,coin -Unique).Where({$_.name -notin ($AllPools.name | Sort -Unique)}))
+ "AllPoolsCount = $($AllPools.Count) After" | out-host
+ # rv LocPools
# Filter Algo based on Per Pool Config
$PoolsConf = $Config.PoolsConfig
- $AllPools = $AllPools | Where {$_.Name -notin ($PoolsConf | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name) -or ($_.Name -in ($PoolsConf | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name) -and ((!($PoolsConf.($_.Name).Algorithm | ? {$_ -like "+*"}) -or ("+$($_.Algorithm)" -in $PoolsConf.($_.Name).Algorithm)) -and ("-$($_.Algorithm)" -notin $PoolsConf.($_.Name).Algorithm)))}
+ $AllPools = $AllPools.Where({$_.Name -notin ($PoolsConf | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name) -or ($_.Name -in ($PoolsConf | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name) -and ((!($PoolsConf.($_.Name).Algorithm | ? {$_ -like "+*"}) -or ("+$($_.Algorithm)" -in $PoolsConf.($_.Name).Algorithm)) -and ("-$($_.Algorithm)" -notin $PoolsConf.($_.Name).Algorithm)))})
- # if($AllPools.Count -eq 0){$Variables.StatusText = "Error contacting pool, retrying.."; $timerCycle.Interval = 15000 ; $timerCycle.Start() ; return}
+ # if($AllPools.Count -eq 0){$Variables.StatusText = "Error contacting pool, retrying.."; $timerCycle.Interval = 15000 ; $timerCycle.Start() ; return}
$Pools = [PSCustomObject]@{}
$Pools_Comparison = [PSCustomObject]@{}
$AllPools.Algorithm | Sort -Unique | ForEach {
+ $Algo = $_
# $Pools | Add-Member $_ ($AllPools | Where Algorithm -EQ $_ | Sort Price -Descending | Select -First 1)
# $Pools_Comparison | Add-Member $_ ($AllPools | Where Algorithm -EQ $_ | Sort StablePrice -Descending | Select -First 1)
- $Pools | Add-Member $_ ($AllPools | Where Algorithm -EQ $_ | Sort Price -Descending)
- $Pools_Comparison | Add-Member $_ ($AllPools | Where Algorithm -EQ $_ | Sort StablePrice -Descending)
+ $Pools | Add-Member $_ ($AllPools.Where({ $_.Algorithm -EQ $Algo }) | Sort Price -Descending)
+ $Pools_Comparison | Add-Member $_ ($AllPools.Where({ $_.Algorithm -EQ $Algo }) | Sort StablePrice -Descending)
}
# $AllPools.Algorithm | Select -Unique | ForEach {$Pools_Comparison | Add-Member $_ ($AllPools | Where Algorithm -EQ $_ | Sort StablePrice -Descending | Select -First 1)}
#Load information about the Miners
@@ -282,7 +356,7 @@ $CycleTime = Measure-Command -Expression {
$NewMiner = &$_.path | select -first 1
$NewMiner | Add-Member -Force @{Name = (Get-Item $_.Path).BaseName}
If ($NewMiner.Path -and (Test-Path (Split-Path $NewMiner.Path))) {
- $Variables.ActiveMinerPrograms | Where { $_.Status -eq "Running" -and $_.Path -eq (Resolve-Path $NewMiner.Path)} | ForEach {
+ $Variables["ActiveMinerPrograms"].Where( { $_.Status -eq "Running" -and (Resolve-Path $_.Path).Path -eq (Resolve-Path $NewMiner.Path).Path} ) | ForEach {
if($_.Process -eq $null)
{
$_.Status = "Failed"
@@ -299,7 +373,8 @@ $CycleTime = Measure-Command -Expression {
$_.Status = "Idle"
}
#Restore Bias for non-active miners
- $Variables.Miners | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments | ForEach {$_.Profit_Bias = $_.Profit_Bias_Orig}
+ $Object = $_
+ $Variables["Miners"].Where({ $_.Path -EQ $Object.Path -and $_.Arguments -EQ $Object.Arguments }) | ForEach {$_.Profit_Bias = $_.Profit_Bias_Orig}
}
# Force re-benchmark - Deactivated
# Get-ChildItem -path ".\stats\" -filter "$($NewMiner.Name)_*.txt" | Remove-Item -Force -Recurse
@@ -312,20 +387,20 @@ $CycleTime = Measure-Command -Expression {
$Variables.StatusText = "Loading miners.."
- $Variables | Add-Member -Force @{Miners = @()}
+ # $Variables | Add-Member -Force @{Miners = @()}
$StartPort=4068
# Better load here than in miner file. Reduces disk reads.
# $MinersConfig = If (Test-Path ".\Config\MinersConfig.json") { Get-content ".\Config\MinersConfig.json" | convertfrom-json }
$Script:MinerCustomConfig = Get-Content ".\Config\MinerCustomConfig.json" | ConvertFrom-Json
$Script:MinerCustomConfigCode = Get-Content ".\Includes\MinerCustomConfig.ps1" -raw
-
- $Variables.Miners = if (Test-Path "Miners") {
- @(
- Get-ChildItemContent "Miners"
- if ($Config.IncludeOptionalMiners -and (Test-Path "OptionalMiners")) {Get-ChildItemContent "OptionalMiners"}
- if (Test-Path "CustomMiners") { Get-ChildItemContent "CustomMiners"}
- ) | ForEach {
+ $i = 0
+ $Variables["Miners"] = if (Test-Path "Miners") {
+ [System.Collections.ArrayList]::Synchronized(@(
+ Get-SubScriptContent "Miners"
+ if ($Config.IncludeOptionalMiners -and (Test-Path "OptionalMiners")) {Get-SubScriptContent "OptionalMiners"}
+ if (Test-Path "CustomMiners") { Get-SubScriptContent "CustomMiners"}
+ )).Where({ $_.Content.Host -ne $Null -and $_.Content.Type -in $Config.Type }).ForEach({
$Miner = $_.Content | Add-Member @{Name = $_.Name} -PassThru
# $Miner = $_
@@ -337,12 +412,22 @@ $CycleTime = Measure-Command -Expression {
$Miner_Profits_Bias = [PSCustomObject]@{}
$Miner_Types = $Miner.Type | Select -Unique
$Miner_Indexes = $Miner.Index | Select -Unique
+ # $Miner.HashRates | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name | ForEach {
+ # $LocPool = $Pools.$_ | Where {$_.Host -eq $Miner.Host -and $_.Coin -eq $Miner.Coin}
+ # $LocPoolsComp = $Pools_Comparison.$_ | Where {$_.Host -eq $Miner.Host -and $_.Coin -eq $Miner.Coin}
+ # $Miner_HashRates | Add-Member $_ ([Double]$Miner.HashRates.$_)
+ # $Miner_Pools | Add-Member $_ ([PSCustomObject]$LocPool)
+ # $Miner_Pools_Comparison | Add-Member $_ ([PSCustomObject]$LocPoolsComp | Where {$_.Host -eq $Miner.Host -and $_.Coin -eq $Miner.Coin})
+ # $Miner_Profits | Add-Member $_ ([Double]$Miner.HashRates.$_*$LocPool.Price)
+ # $Miner_Profits_Comparison | Add-Member $_ ([Double]$Miner.HashRates.$_*$LocPoolsComp.Price)
+ # $Miner_Profits_Bias | Add-Member $_ ([Double]$Miner.HashRates.$_*$LocPool.Price*(1-($Config.MarginOfError*[Math]::Pow($Variables.DecayBase,$DecayExponent))))
+ # }
$Miner.HashRates | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name | ForEach {
- $LocPool = $Pools.$_ | ? {$_.Host -eq $Miner.Host -and $_.Coin -eq $Miner.Coin}
- $LocPoolsComp = $Pools_Comparison.$_ | ? {$_.Host -eq $Miner.Host -and $_.Coin -eq $Miner.Coin}
+ $LocPool = $Pools.$_ | Where {$_.Host -in $Miner.Host -and $_.Coin -in $Miner.Coin}
+ $LocPoolsComp = $Pools_Comparison.$_ | Where {$_.Host -in $Miner.Host -and $_.Coin -in $Miner.Coin}
$Miner_HashRates | Add-Member $_ ([Double]$Miner.HashRates.$_)
$Miner_Pools | Add-Member $_ ([PSCustomObject]$LocPool)
- $Miner_Pools_Comparison | Add-Member $_ ([PSCustomObject]$LocPoolsComp | ? {$_.Host -eq $Miner.Host -and $_.Coin -eq $Miner.Coin})
+ $Miner_Pools_Comparison | Add-Member $_ ([PSCustomObject]$LocPoolsComp | Where {$_.Host -in $Miner.Host -and $_.Coin -in $Miner.Coin} )
$Miner_Profits | Add-Member $_ ([Double]$Miner.HashRates.$_*$LocPool.Price)
$Miner_Profits_Comparison | Add-Member $_ ([Double]$Miner.HashRates.$_*$LocPoolsComp.Price)
$Miner_Profits_Bias | Add-Member $_ ([Double]$Miner.HashRates.$_*$LocPool.Price*(1-($Config.MarginOfError*[Math]::Pow($Variables.DecayBase,$DecayExponent))))
@@ -362,8 +447,8 @@ $CycleTime = Measure-Command -Expression {
$Miner_Profit_Bias = $null
}
}
- if($Miner_Types -eq $null){$Miner_Types = $Variables.Miners.Type | Select -Unique}
- if($Miner_Indexes -eq $null){$Miner_Indexes = $Variables.Miners.Index | Select -Unique}
+ if($Miner_Types -eq $null){$Miner_Types = $Variables["Miners"].Type | Select -Unique}
+ if($Miner_Indexes -eq $null){$Miner_Indexes = $Variables["Miners"].Index | Select -Unique}
if($Miner_Types -eq $null){$Miner_Types = ""}
if($Miner_Indexes -eq $null){$Miner_Indexes = 0}
$Miner.HashRates = $Miner_HashRates
@@ -380,24 +465,23 @@ $CycleTime = Measure-Command -Expression {
# $Miner.Path = Convert-Path $Miner.Path
$Miner_Devices = $Miner.Device | Select -Unique
- if($Miner_Devices -eq $null){$Miner_Devices = ($Variables.Miners | Where {(Compare $Miner.Type $_.Type -IncludeEqual -ExcludeDifferent | Measure).Count -gt 0}).Device | Select -Unique}
+ if($Miner_Devices -eq $null){$Miner_Devices = ($Variables["Miners"].Where({(Compare $Miner.Type $_.Type -IncludeEqual -ExcludeDifferent | Measure).Count -gt 0})).Device | Select -Unique}
if($Miner_Devices -eq $null){$Miner_Devices = $Miner.Type}
$Miner | Add-Member Device $Miner_Devices -Force
$Miner
- } |
- Where {$Config.Type.Count -eq 0 -or (Compare $Config.Type $_.Type -IncludeEqual -ExcludeDifferent | Measure).Count -gt 0} |
- Where {!($Config.Algorithm | ? {$_.StartsWith("+")}) -or (Compare (($Config.Algorithm | ? {$_.StartsWith("+")}).Replace("+", "")) $_.HashRates.PSObject.Properties.Name -IncludeEqual -ExcludeDifferent | Measure).Count -gt 0} |
- Where {$Config.MinerName.Count -eq 0 -or (Compare $Config.MinerName $_.Name -IncludeEqual -ExcludeDifferent | Measure).Count -gt 0}
+ }).Where(
+ {$Config.Type.Count -eq 0 -or (Compare $Config.Type $_.Type -IncludeEqual -ExcludeDifferent | Measure).Count -gt 0}).Where(
+ {$Config.MinerName.Count -eq 0 -or (Compare $Config.MinerName $_.Name -IncludeEqual -ExcludeDifferent | Measure).Count -gt 0})
}
# 5.2.1
# Added sceurity to filter miners with no user name in case of malformed miner or pool file
- $Variables.Miners = $Variables.Miners | ? {$_.User}
+ $Variables["Miners"] = $Variables["Miners"].Where({$_.User})
# 5.2.1
# Exclude non benchmarked during donation.
- If ($Variables.DonationRunning) {
- $Variables.Miners = $Variables.Miners | ? {$_.HashRates.Psobject.properties.value}
+ If ($Variables.DonationStart -or $Variables.DonationRunning) {
+ $Variables["Miners"] = $Variables["Miners"].Where({$_.HashRates.Psobject.properties.value})
}
@@ -408,14 +492,14 @@ $CycleTime = Measure-Command -Expression {
# ** Ban is not persistent across sessions **
If ($Config.MaxMinerFailure -gt 0){
$Config | Add-Member -Force @{ MaxMinerFailure = If ($Config.MaxMinerFailure) {$Config.MaxMinerFailure} else {3} }
- $BannedMiners = $Variables.ActiveMinerPrograms | Where { $_.Status -eq "Failed" -and $_.FailedCount -ge $Config.MaxMinerFailure }
+ $BannedMiners = $Variables["ActiveMinerPrograms"].Where( { $_.Status -eq "Failed" -and $_.FailedCount -ge $Config.MaxMinerFailure } )
# $BannedMiners | foreach { $Variables.StatusText = "BANNED: $($_.Name) / $($_.Algorithms). Too many failures. Consider Algo exclusion in config." }
$BannedMiners | foreach { "BANNED: $($_.Name) / $($_.Algorithms). Too many failures. Consider Algo exclusion in config." | Out-Host }
- $Variables.Miners = $Variables.Miners | Where { $_.Path -notin $BannedMiners.Path -and $_.Arguments -notin $BannedMiners.Arguments }
+ $Variables["Miners"] = $Variables["Miners"].Where( { $_.Path -notin $BannedMiners.Path -and $_.Arguments -notin $BannedMiners.Arguments } )
}
- $Variables.Miners | ? { (Test-Path $_.Path) -eq $false } | ForEach {
+ ($Variables["Miners"] | Sort Path,URI -Unique).Where({ (Test-Path $_.Path) -eq $false }) | ForEach {
$Miner = $_
if((Test-Path $Miner.Path) -eq $false)
{
@@ -423,7 +507,7 @@ $CycleTime = Measure-Command -Expression {
if((Split-Path $Miner.URI -Leaf) -eq (Split-Path $Miner.Path -Leaf))
{
New-Item (Split-Path $Miner.Path) -ItemType "Directory" | Out-Null
- Invoke-WebRequest $Miner.URI -TimeoutSec 15 -OutFile $_.Path -UseBasicParsing
+ Invoke-WebRequest $Miner.URI -UseBasicParsing -TimeoutSec 15 -OutFile $_.Path
}
elseif(([IO.FileInfo](Split-Path $_.URI -Leaf)).Extension -eq '')
{
@@ -452,196 +536,214 @@ $CycleTime = Measure-Command -Expression {
}
- $Variables.Miners = $Variables.Miners | ? { (Test-Path $_.Path) -eq $true }
-
- $Variables.StatusText = "Comparing miners and pools.."
- if($Variables.Miners.Count -eq 0){$Variables.StatusText = "No Miners!"}#; sleep $Config.Interval; continue}
-
- # Remove miners when no estimation info from pools or 0BTC. Avoids mining when algo down at pool or benchmarking for ever
- If (($Variables.Miners | ? {($_.Pools.PSObject.Properties.Value.Price -ne $null) -and ($_.Pools.PSObject.Properties.Value.Price -gt 0)}).Count -gt 0) {$Variables.Miners = $Variables.Miners | ? {($_.Pools.PSObject.Properties.Value.Price -ne $null) -and ($_.Pools.PSObject.Properties.Value.Price -gt 0)}}
- #Don't penalize active miners. Miner could switch a little bit later and we will restore his bias in this case
- $Variables.ActiveMinerPrograms | Where { $_.Status -eq "Running" } | ForEach {$Variables.Miners | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments | ForEach {$_.Profit_Bias = $_.Profit * (1 + $Config.ActiveMinerGainPct / 100)}}
- #Get most profitable miner combination i.e. AMD+NVIDIA+CPU
- $BestMiners = $Variables.Miners | Select Type,Index -Unique | ForEach {$Miner_GPU = $_; ($Variables.Miners | Where {(Compare $Miner_GPU.Type $_.Type | Measure).Count -eq 0 -and (Compare $Miner_GPU.Index $_.Index | Measure).Count -eq 0} | Sort -Descending {($_ | Where Profit -EQ $null | Measure).Count},{($_ | Measure Profit_Bias -Sum).Sum},{($_ | Where Profit -NE 0 | Measure).Count} | Select -First 1)}
- $BestDeviceMiners = $Variables.Miners | Select Device -Unique | ForEach {$Miner_GPU = $_; ($Variables.Miners | Where {(Compare $Miner_GPU.Device $_.Device | Measure).Count -eq 0} | Sort -Descending {($_ | Where Profit -EQ $null | Measure).Count},{($_ | Measure Profit_Bias -Sum).Sum},{($_ | Where Profit -NE 0 | Measure).Count} | Select -First 1)}
- $BestMiners_Comparison = $Variables.Miners | Select Type,Index -Unique | ForEach {$Miner_GPU = $_; ($Variables.Miners | Where {(Compare $Miner_GPU.Type $_.Type | Measure).Count -eq 0 -and (Compare $Miner_GPU.Index $_.Index | Measure).Count -eq 0} | Sort -Descending {($_ | Where Profit -EQ $null | Measure).Count},{($_ | Measure Profit_Comparison -Sum).Sum},{($_ | Where Profit -NE 0 | Measure).Count} | Select -First 1)}
- $BestDeviceMiners_Comparison = $Variables.Miners | Select Device -Unique | ForEach {$Miner_GPU = $_; ($Variables.Miners | Where {(Compare $Miner_GPU.Device $_.Device | Measure).Count -eq 0} | Sort -Descending {($_ | Where Profit -EQ $null | Measure).Count},{($_ | Measure Profit_Comparison -Sum).Sum},{($_ | Where Profit -NE 0 | Measure).Count} | Select -First 1)}
- $Miners_Type_Combos = @([PSCustomObject]@{Combination = @()}) + (Get-Combination ($Variables.Miners | Select Type -Unique) | Where{(Compare ($_.Combination | Select -ExpandProperty Type -Unique) ($_.Combination | Select -ExpandProperty Type) | Measure).Count -eq 0})
- $Miners_Index_Combos = @([PSCustomObject]@{Combination = @()}) + (Get-Combination ($Variables.Miners | Select Index -Unique) | Where{(Compare ($_.Combination | Select -ExpandProperty Index -Unique) ($_.Combination | Select -ExpandProperty Index) | Measure).Count -eq 0})
- $Miners_Device_Combos = (Get-Combination ($Variables.Miners | Select Device -Unique) | Where{(Compare ($_.Combination | Select -ExpandProperty Device -Unique) ($_.Combination | Select -ExpandProperty Device) | Measure).Count -eq 0})
- $BestMiners_Combos = $Miners_Type_Combos | ForEach {$Miner_Type_Combo = $_.Combination; $Miners_Index_Combos | ForEach {$Miner_Index_Combo = $_.Combination; [PSCustomObject]@{Combination = $Miner_Type_Combo | ForEach {$Miner_Type_Count = $_.Type.Count; [Regex]$Miner_Type_Regex = '^(' + (($_.Type | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $Miner_Index_Combo | ForEach {$Miner_Index_Count = $_.Index.Count; [Regex]$Miner_Index_Regex = '^(' + (($_.Index | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $BestMiners | Where {([Array]$_.Type -notmatch $Miner_Type_Regex).Count -eq 0 -and ([Array]$_.Index -notmatch $Miner_Index_Regex).Count -eq 0 -and ([Array]$_.Type -match $Miner_Type_Regex).Count -eq $Miner_Type_Count -and ([Array]$_.Index -match $Miner_Index_Regex).Count -eq $Miner_Index_Count}}}}}}
- $BestMiners_Combos += $Miners_Device_Combos | ForEach {$Miner_Device_Combo = $_.Combination; [PSCustomObject]@{Combination = $Miner_Device_Combo | ForEach {$Miner_Device_Count = $_.Device.Count; [Regex]$Miner_Device_Regex = '^(' + (($_.Device | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $BestDeviceMiners | Where {([Array]$_.Device -notmatch $Miner_Device_Regex).Count -eq 0 -and ([Array]$_.Device -match $Miner_Device_Regex).Count -eq $Miner_Device_Count}}}}
- $BestMiners_Combos_Comparison = $Miners_Type_Combos | ForEach {$Miner_Type_Combo = $_.Combination; $Miners_Index_Combos | ForEach {$Miner_Index_Combo = $_.Combination; [PSCustomObject]@{Combination = $Miner_Type_Combo | ForEach {$Miner_Type_Count = $_.Type.Count; [Regex]$Miner_Type_Regex = '^(' + (($_.Type | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $Miner_Index_Combo | ForEach {$Miner_Index_Count = $_.Index.Count; [Regex]$Miner_Index_Regex = '^(' + (($_.Index | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $BestMiners_Comparison | Where {([Array]$_.Type -notmatch $Miner_Type_Regex).Count -eq 0 -and ([Array]$_.Index -notmatch $Miner_Index_Regex).Count -eq 0 -and ([Array]$_.Type -match $Miner_Type_Regex).Count -eq $Miner_Type_Count -and ([Array]$_.Index -match $Miner_Index_Regex).Count -eq $Miner_Index_Count}}}}}}
- $BestMiners_Combos_Comparison += $Miners_Device_Combos | ForEach {$Miner_Device_Combo = $_.Combination; [PSCustomObject]@{Combination = $Miner_Device_Combo | ForEach {$Miner_Device_Count = $_.Device.Count; [Regex]$Miner_Device_Regex = '^(' + (($_.Device | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $BestDeviceMiners_Comparison | Where {([Array]$_.Device -notmatch $Miner_Device_Regex).Count -eq 0 -and ([Array]$_.Device -match $Miner_Device_Regex).Count -eq $Miner_Device_Count}}}}
- $BestMiners_Combo = $BestMiners_Combos | Sort -Descending {($_.Combination | Where Profit -EQ $null | Measure).Count},{($_.Combination | Measure Profit_Bias -Sum).Sum},{($_.Combination | Where Profit -NE 0 | Measure).Count} | Select -First 1 | Select -ExpandProperty Combination
- $BestMiners_Combo_Comparison = $BestMiners_Combos_Comparison | Sort -Descending {($_.Combination | Where Profit -EQ $null | Measure).Count},{($_.Combination | Measure Profit_Comparison -Sum).Sum},{($_.Combination | Where Profit -NE 0 | Measure).Count} | Select -First 1 | Select -ExpandProperty Combination
- # No CPU mining if GPU miner prevents it
- If ($BestMiners_Combo.PreventCPUMining -contains $true) {
- $BestMiners_Combo = $BestMiners_Combo | ? {$_.type -ne "CPU"}
- $Variables.StatusText = "Miner prevents CPU mining"
- }
-
- If ($Variables.DonationRunning) {
- $BestMiners_Combo | % {
- $_.Arguments = $_.Arguments -replace "$($Config.PoolsConfig.Default.WorkerName)","$($Config.PoolsConfig.Default.WorkerName)_$($_.Type)"
+ $Variables["Miners"] = $Variables["Miners"].Where({ (Test-Path $_.Path) -eq $true })
+
+ # If (! $Variables.DonationRunning) {
+ $Variables.StatusText = "Comparing miners and pools.."
+ if($Variables["Miners"].Count -eq 0){$Variables.StatusText = "No Miners!"}#; sleep $Config.Interval; continue}
+
+ # Remove miners when no estimation info from pools or 0BTC. Avoids mining when algo down at pool or benchmarking for ever
+ If (($Variables["Miners"] | ? {($_.Pools.PSObject.Properties.Value.Price -ne $null) -and ($_.Pools.PSObject.Properties.Value.Price -gt 0)}).Count -gt 0) {$Variables["Miners"] = $Variables["Miners"] | ? {($_.Pools.PSObject.Properties.Value.Price -ne $null) -and ($_.Pools.PSObject.Properties.Value.Price -gt 0)}}
+ #Don't penalize active miners. Miner could switch a little bit later and we will restore his bias in this case
+ $Variables["ActiveMinerPrograms"] | Where { $_.Status -eq "Running" } | ForEach {$Variables["Miners"] | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments | ForEach {$_.Profit_Bias = $_.Profit * (1 + $Config.ActiveMinerGainPct / 100)}}
+ #Get most profitable miner combination i.e. AMD+NVIDIA+CPU
+ $BestMiners = $Variables["Miners"] | Select Type,Index -Unique | ForEach {$Miner_GPU = $_; ($Variables["Miners"] | Where {(Compare $Miner_GPU.Type $_.Type | Measure).Count -eq 0 -and (Compare $Miner_GPU.Index $_.Index | Measure).Count -eq 0} | Sort -Descending {($_ | Where Profit -EQ $null | Measure).Count},{($_ | Measure Profit_Bias -Sum).Sum},{($_ | Where Profit -NE 0 | Measure).Count} | Select -First 1)}
+ $BestDeviceMiners = $Variables["Miners"] | Select Device -Unique | ForEach {$Miner_GPU = $_; ($Variables["Miners"] | Where {(Compare $Miner_GPU.Device $_.Device | Measure).Count -eq 0} | Sort -Descending {($_ | Where Profit -EQ $null | Measure).Count},{($_ | Measure Profit_Bias -Sum).Sum},{($_ | Where Profit -NE 0 | Measure).Count} | Select -First 1)}
+ $BestMiners_Comparison = $Variables["Miners"] | Select Type,Index -Unique | ForEach {$Miner_GPU = $_; ($Variables["Miners"] | Where {(Compare $Miner_GPU.Type $_.Type | Measure).Count -eq 0 -and (Compare $Miner_GPU.Index $_.Index | Measure).Count -eq 0} | Sort -Descending {($_ | Where Profit -EQ $null | Measure).Count},{($_ | Measure Profit_Comparison -Sum).Sum},{($_ | Where Profit -NE 0 | Measure).Count} | Select -First 1)}
+ $BestDeviceMiners_Comparison = $Variables["Miners"] | Select Device -Unique | ForEach {$Miner_GPU = $_; ($Variables["Miners"] | Where {(Compare $Miner_GPU.Device $_.Device | Measure).Count -eq 0} | Sort -Descending {($_ | Where Profit -EQ $null | Measure).Count},{($_ | Measure Profit_Comparison -Sum).Sum},{($_ | Where Profit -NE 0 | Measure).Count} | Select -First 1)}
+ $Miners_Type_Combos = @([PSCustomObject]@{Combination = @()}) + (Get-Combination ($Variables["Miners"] | Select Type -Unique) | Where{(Compare ($_.Combination | Select -ExpandProperty Type -Unique) ($_.Combination | Select -ExpandProperty Type) | Measure).Count -eq 0})
+ $Miners_Index_Combos = @([PSCustomObject]@{Combination = @()}) + (Get-Combination ($Variables["Miners"] | Select Index -Unique) | Where{(Compare ($_.Combination | Select -ExpandProperty Index -Unique) ($_.Combination | Select -ExpandProperty Index) | Measure).Count -eq 0})
+ $Miners_Device_Combos = (Get-Combination ($Variables["Miners"] | Select Device -Unique) | Where{(Compare ($_.Combination | Select -ExpandProperty Device -Unique) ($_.Combination | Select -ExpandProperty Device) | Measure).Count -eq 0})
+ $BestMiners_Combos = $Miners_Type_Combos | ForEach {$Miner_Type_Combo = $_.Combination; $Miners_Index_Combos | ForEach {$Miner_Index_Combo = $_.Combination; [PSCustomObject]@{Combination = $Miner_Type_Combo | ForEach {$Miner_Type_Count = $_.Type.Count; [Regex]$Miner_Type_Regex = '^(' + (($_.Type | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $Miner_Index_Combo | ForEach {$Miner_Index_Count = $_.Index.Count; [Regex]$Miner_Index_Regex = '^(' + (($_.Index | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $BestMiners | Where {([Array]$_.Type -notmatch $Miner_Type_Regex).Count -eq 0 -and ([Array]$_.Index -notmatch $Miner_Index_Regex).Count -eq 0 -and ([Array]$_.Type -match $Miner_Type_Regex).Count -eq $Miner_Type_Count -and ([Array]$_.Index -match $Miner_Index_Regex).Count -eq $Miner_Index_Count}}}}}}
+ $BestMiners_Combos += $Miners_Device_Combos | ForEach {$Miner_Device_Combo = $_.Combination; [PSCustomObject]@{Combination = $Miner_Device_Combo | ForEach {$Miner_Device_Count = $_.Device.Count; [Regex]$Miner_Device_Regex = '^(' + (($_.Device | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $BestDeviceMiners | Where {([Array]$_.Device -notmatch $Miner_Device_Regex).Count -eq 0 -and ([Array]$_.Device -match $Miner_Device_Regex).Count -eq $Miner_Device_Count}}}}
+ $BestMiners_Combos_Comparison = $Miners_Type_Combos | ForEach {$Miner_Type_Combo = $_.Combination; $Miners_Index_Combos | ForEach {$Miner_Index_Combo = $_.Combination; [PSCustomObject]@{Combination = $Miner_Type_Combo | ForEach {$Miner_Type_Count = $_.Type.Count; [Regex]$Miner_Type_Regex = '^(' + (($_.Type | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $Miner_Index_Combo | ForEach {$Miner_Index_Count = $_.Index.Count; [Regex]$Miner_Index_Regex = '^(' + (($_.Index | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $BestMiners_Comparison | Where {([Array]$_.Type -notmatch $Miner_Type_Regex).Count -eq 0 -and ([Array]$_.Index -notmatch $Miner_Index_Regex).Count -eq 0 -and ([Array]$_.Type -match $Miner_Type_Regex).Count -eq $Miner_Type_Count -and ([Array]$_.Index -match $Miner_Index_Regex).Count -eq $Miner_Index_Count}}}}}}
+ $BestMiners_Combos_Comparison += $Miners_Device_Combos | ForEach {$Miner_Device_Combo = $_.Combination; [PSCustomObject]@{Combination = $Miner_Device_Combo | ForEach {$Miner_Device_Count = $_.Device.Count; [Regex]$Miner_Device_Regex = '^(' + (($_.Device | ForEach {[Regex]::Escape($_)}) -join '|') + ')$'; $BestDeviceMiners_Comparison | Where {([Array]$_.Device -notmatch $Miner_Device_Regex).Count -eq 0 -and ([Array]$_.Device -match $Miner_Device_Regex).Count -eq $Miner_Device_Count}}}}
+ $BestMiners_Combo = $BestMiners_Combos | Sort -Descending {($_.Combination | Where Profit -EQ $null | Measure).Count},{($_.Combination | Measure Profit_Bias -Sum).Sum},{($_.Combination | Where Profit -NE 0 | Measure).Count} | Select -First 1 | Select -ExpandProperty Combination
+ $BestMiners_Combo_Comparison = $BestMiners_Combos_Comparison | Sort -Descending {($_.Combination | Where Profit -EQ $null | Measure).Count},{($_.Combination | Measure Profit_Comparison -Sum).Sum},{($_.Combination | Where Profit -NE 0 | Measure).Count} | Select -First 1 | Select -ExpandProperty Combination
+ # No CPU mining if GPU miner prevents it
+ If ($BestMiners_Combo.PreventCPUMining -contains $true) {
+ $BestMiners_Combo = $BestMiners_Combo | ? {$_.type -ne "CPU"}
+ $Variables.StatusText = "Miner prevents CPU mining"
+ }
+ # }
+
+ # Prevent switching during donation
+ If ($Variables.DonationStart -or $Variables.DonationRunning) {
+ If ($Variables.DonationRunning) {$BestMiners_Combo = $Variables.DonationBestMiners_Combo}
+ If ($Variables.DonationStart) {
+ $BestMiners_Combo | % {
+ $_.Arguments = $_.Arguments -replace "$($Config.PoolsConfig.Default.WorkerName)","$($Config.PoolsConfig.Default.WorkerName)_$($_.Type)"
+ }
+ $Variables.DonationBestMiners_Combo = $BestMiners_Combo
}
}
$Variables.StatusText = "Assigning miners.."
#Add the most profitable miners to the active list
- $BestMiners_Combo | ForEach {
- if(($Variables.ActiveMinerPrograms | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments).Count -eq 0)
- {
- $Variables.ActiveMinerPrograms += [PSCustomObject]@{
- Type = $_.Type
- Name = $_.Name
- Path = $_.Path
- Arguments = $_.Arguments
- Wrap = $_.Wrap
- Process = $null
- API = $_.API
- Port = $_.Port
- Algorithms = $_.HashRates.PSObject.Properties.Name
- New = $false
- Active = [TimeSpan]0
- TotalActive = [TimeSpan]0
- Activated = 0
- FailedCount = 0
- Status = "Idle"
- HashRate = 0
- Benchmarked = 0
- Hashrate_Gathered = ($_.HashRates.PSObject.Properties.Value -ne $null)
- User = $_.User
- Host = $_.Host
- Coin = $_.Coin
+ # Prevent switching during donation
+ # If (! $Variables.DonationRunning -or ($Variables["ActiveMinerPrograms"] | ? {$_.Status -ne "Idle" -and ($_.Process -eq $null -or $_.Process.HasExited -ne $false})).Count -gt 0) {
+ # If (! $Variables.DonationRunning) {
+ $BestMiners_Combo | ForEach {
+ if(($Variables["ActiveMinerPrograms"] | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments).Count -eq 0)
+ {
+ $Variables["ActiveMinerPrograms"] += [PSCustomObject]@{
+ Type = $_.Type
+ Name = $_.Name
+ Path = $_.Path
+ Arguments = $_.Arguments
+ Wrap = $_.Wrap
+ Process = $null
+ API = $_.API
+ Port = $_.Port
+ Algorithms = $_.HashRates.PSObject.Properties.Name
+ New = $false
+ Active = [TimeSpan]0
+ TotalActive = [TimeSpan]0
+ Activated = 0
+ FailedCount = 0
+ Status = "Idle"
+ HashRate = 0
+ Benchmarked = 0
+ Hashrate_Gathered = ($_.HashRates.PSObject.Properties.Value -ne $null)
+ User = $_.User
+ Host = $_.Host
+ Coin = $_.Coin
+ Pools = $_.Pools
+ }
}
}
- }
- #Stop or start miners in the active list depending on if they are the most profitable
- # We have to stop processes first or the port would be busy
- $Variables.ActiveMinerPrograms | ForEach {
- [Array]$filtered = ($BestMiners_Combo | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments)
- if($filtered.Count -eq 0)
- {
- if($_.Process -eq $null)
+ #Stop or start miners in the active list depending on if they are the most profitable
+ # We have to stop processes first or the port would be busy
+ $Variables["ActiveMinerPrograms"] | ForEach {
+ [Array]$filtered = ($BestMiners_Combo | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments)
+ if($filtered.Count -eq 0)
{
- $_.Status = "Failed"
- $_.FailedCount++
- }
- elseif ($_.Process.HasExited -eq $false) {
- $_.Process.CloseMainWindow() | Out-Null
- Sleep 1
- # simply "Kill with power"
- Stop-Process $_.Process -Force | Out-Null
- # Try to kill any process with the same path, in case it is still running but the process handle is incorrect
- $KillPath = $_.Path
- Get-Process | Where-Object {$_.Path -eq $KillPath} | Stop-Process -Force
- Write-Host -ForegroundColor Yellow "closing miner"
- Sleep 1
- $_.Status = "Idle"
+ if($_.Process -eq $null)
+ {
+ $_.Status = "Failed"
+ $_.FailedCount++
+ }
+ elseif ($_.Process.HasExited -eq $false) {
+ $_.Process.CloseMainWindow() | Out-Null
+ Sleep 1
+ # simply "Kill with power"
+ Stop-Process $_.Process -Force | Out-Null
+ # Try to kill any process with the same path, in case it is still running but the process handle is incorrect
+ $KillPath = $_.Path
+ Get-Process | Where-Object {$_.Path -eq $KillPath} | Stop-Process -Force
+ Write-Host -ForegroundColor Yellow "closing miner"
+ Sleep 1
+ $_.Status = "Idle"
+ }
+ #Restore Bias for non-active miners
+ $Variables["Miners"] | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments | ForEach {$_.Profit_Bias = $_.Profit_Bias_Orig}
}
- #Restore Bias for non-active miners
- $Variables.Miners | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments | ForEach {$_.Profit_Bias = $_.Profit_Bias_Orig}
}
- }
- $newMiner = $false
- $CurrentMinerHashrate_Gathered =$false
- $newMiner = $false
- $CurrentMinerHashrate_Gathered =$false
- $Variables.ActiveMinerPrograms | ForEach {
- [Array]$filtered = ($BestMiners_Combo | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments)
- if($filtered.Count -gt 0)
- {
- if($_.Process -eq $null -or $_.Process.HasExited -ne $false)
+ $newMiner = $false
+ $CurrentMinerHashrate_Gathered =$false
+ $newMiner = $false
+ $CurrentMinerHashrate_Gathered =$false
+ $Variables["ActiveMinerPrograms"] | ForEach {
+ [Array]$filtered = ($BestMiners_Combo | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments)
+ if($filtered.Count -gt 0)
{
- # Migrate previous version of .\log\switching.log (Add Coin)
- # Move to app init
- If (!(Get-Content .\Logs\switching.log -First 1).Contains("coin")) {
- $tmp = @()
- Import-Csv .\Logs\switching.log | % {$tmp += [PsCustomObject][Ordered]@{date=$_.date;Type=$_.Type;algo=$_.Algo;coin=$_.Coin;wallet=$_.wallet;username=$_.UserName;Host=$_.host}}
- $tmp | export-csv .\Logs\switching.log -NoTypeInformation -Force
- rv tmp
- }
- # Log switching information to .\log\switching.log
- [pscustomobject]@{date=(get-date);Type=$_.Type;algo=$_.Algorithms;coin=$_.Coin;wallet=$_.User;username=$Config.UserName;Host=$_.host} | export-csv .\Logs\switching.log -Append -NoTypeInformation -Force
+ if($_.Process -eq $null -or $_.Process.HasExited -ne $false)
+ {
+ # Migrate previous version of .\log\switching.log (Add Coin)
+ # Move to app init
+ If (Test-Path ".\Logs\switching.log") {
+ If (!(Get-Content ".\Logs\switching.log" -First 1).Contains("coin")) {
+ $tmp = @()
+ Import-Csv .\Logs\switching.log | % {$tmp += [PsCustomObject][Ordered]@{date=$_.date;Type=$_.Type;algo=$_.Algo;coin=$_.Coin;wallet=$_.wallet;username=$_.UserName;Host=$_.host}}
+ $tmp | export-csv .\Logs\switching.log -NoTypeInformation -Force
+ rv tmp
+ }
+ }
+ # Log switching information to .\log\switching.log
+ [pscustomobject]@{date=(get-date);Type=$_.Type;algo=$_.Algorithms -join ',';coin=$_.Coin -join ',';wallet=$_.User -join ',';username=$Config.UserName -join ',';Host=$_.host -join ','} | export-csv .\Logs\switching.log -Append -NoTypeInformation -Force
+ If ($Variables.DonationStart) {
+ $Variables.DonationStart = $False
+ $Variables.DonationRunning = $True
+ }
- # Launch prerun if exists
- If ($_.Type -eq "AMD" -and (Test-Path ".\Prerun\AMDPrerun.bat")) {
- Start-Process ".\Prerun\AMDPrerun.bat" -WorkingDirectory ".\Prerun" -WindowStyle hidden
- }
- If ($_.Type -eq "NVIDIA" -and (Test-Path ".\Prerun\NVIDIAPrerun.bat")) {
- Start-Process ".\Prerun\NVIDIAPrerun.bat" -WorkingDirectory ".\Prerun" -WindowStyle hidden
- }
- If ($_.Type -eq "CPU" -and (Test-Path ".\Prerun\CPUPrerun.bat")) {
- Start-Process ".\Prerun\CPUPrerun.bat" -WorkingDirectory ".\Prerun" -WindowStyle hidden
- }
- If ($_.Type -ne "CPU") {
- $PrerunName = ".\Prerun\"+$_.Algorithms+".bat"
- $DefaultPrerunName = ".\Prerun\default.bat"
- If (Test-Path $PrerunName) {
- $Variables.StatusText = "Launching Prerun: $PrerunName"
- Start-Process $PrerunName -WorkingDirectory ".\Prerun" -WindowStyle hidden
- Sleep 2
- } else {
- If (Test-Path $DefaultPrerunName) {
- $Variables.StatusText = "Launching Prerun: $DefaultPrerunName"
- Start-Process $DefaultPrerunName -WorkingDirectory ".\Prerun" -WindowStyle hidden
+ # Launch prerun if exists
+ If ($_.Type -eq "AMD" -and (Test-Path ".\Prerun\AMDPrerun.bat")) {
+ Start-Process ".\Prerun\AMDPrerun.bat" -WorkingDirectory ".\Prerun" -WindowStyle hidden
+ }
+ If ($_.Type -eq "NVIDIA" -and (Test-Path ".\Prerun\NVIDIAPrerun.bat")) {
+ Start-Process ".\Prerun\NVIDIAPrerun.bat" -WorkingDirectory ".\Prerun" -WindowStyle hidden
+ }
+ If ($_.Type -eq "CPU" -and (Test-Path ".\Prerun\CPUPrerun.bat")) {
+ Start-Process ".\Prerun\CPUPrerun.bat" -WorkingDirectory ".\Prerun" -WindowStyle hidden
+ }
+ If ($_.Type -ne "CPU") {
+ $PrerunName = ".\Prerun\"+$_.Algorithms+".bat"
+ $DefaultPrerunName = ".\Prerun\default.bat"
+ If (Test-Path $PrerunName) {
+ $Variables.StatusText = "Launching Prerun: $PrerunName"
+ Start-Process $PrerunName -WorkingDirectory ".\Prerun" -WindowStyle hidden
Sleep 2
- }
+ } else {
+ If (Test-Path $DefaultPrerunName) {
+ $Variables.StatusText = "Launching Prerun: $DefaultPrerunName"
+ Start-Process $DefaultPrerunName -WorkingDirectory ".\Prerun" -WindowStyle hidden
+ Sleep 2
+ }
+ }
}
- }
- Sleep $Config.Delay #Wait to prevent BSOD
- $Variables.StatusText = "Starting miner"
- $Variables.DecayStart = Get-Date
- $_.New = $true
- $_.Activated++
- # if($_.Process -ne $null){$_.TotalActive += $_.Process.ExitTime-$_.Process.StartTime}
- if($_.Process -ne $null){$_.Active = [TimeSpan]0}
-
- if($_.Wrap){$_.Process = Start-Process -FilePath "PowerShell" -ArgumentList "-executionpolicy bypass -command . '$(Convert-Path ".\Includes\Wrapper.ps1")' -ControllerProcessID $PID -Id '$($_.Port)' -FilePath '$($_.Path)' -ArgumentList '$($_.Arguments)' -WorkingDirectory '$(Split-Path $_.Path)'" -PassThru}
- else{$_.Process = Start-SubProcess -FilePath $_.Path -ArgumentList $_.Arguments -WorkingDirectory (Split-Path $_.Path)}
- if($_.Process -eq $null){$_.Status = "Failed";$_.FailedCount++}
- else {
- $_.Status = "Running"
- $newMiner = $true
- #Newely started miner should looks better than other in the first run too
- $Variables.Miners | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments | ForEach {$_.Profit_Bias = $_.Profit * (1 + $Config.ActiveMinerGainPct / 100)}
+ Sleep $Config.Delay #Wait to prevent BSOD
+ $Variables.StatusText = "Starting miner"
+ $Variables.DecayStart = Get-Date
+ $_.New = $true
+ $_.Activated++
+ # if($_.Process -ne $null){$_.TotalActive += $_.Process.ExitTime-$_.Process.StartTime}
+ if($_.Process -ne $null){$_.Active = [TimeSpan]0}
+
+ if($_.Wrap){$_.Process = Start-Process -FilePath "PowerShell" -ArgumentList "-executionpolicy bypass -command . '$(Convert-Path ".\Includes\Wrapper.ps1")' -ControllerProcessID $PID -Id '$($_.Port)' -FilePath '$($_.Path)' -ArgumentList '$($_.Arguments)' -WorkingDirectory '$(Split-Path $_.Path)'" -PassThru}
+ else{$_.Process = Start-SubProcess -FilePath $_.Path -ArgumentList $_.Arguments -WorkingDirectory (Split-Path $_.Path)}
+ if($_.Process -eq $null){$_.Status = "Failed";$_.FailedCount++}
+ else {
+ $_.Status = "Running"
+ $newMiner = $true
+ #Newely started miner should looks better than other in the first run too
+ $Variables["Miners"] | Where Path -EQ $_.Path | Where Arguments -EQ $_.Arguments | ForEach {$_.Profit_Bias = $_.Profit * (1 + $Config.ActiveMinerGainPct / 100)}
+ }
+ } else {
+ $now = Get-Date
+ $_.TotalActive = $_.TotalActive + ( $Now - $_.Process.StartTime ) - $_.Active
+ $_.Active = $Now - $_.Process.StartTime
}
- } else {
- $now = Get-Date
- $_.TotalActive = $_.TotalActive + ( $Now - $_.Process.StartTime ) - $_.Active
- $_.Active = $Now - $_.Process.StartTime
+ $CurrentMinerHashrate_Gathered = $_.Hashrate_Gathered
}
- $CurrentMinerHashrate_Gathered = $_.Hashrate_Gathered
}
- }
+ # }
#Do nothing for a few seconds as to not overload the APIs
if ($newMiner -eq $true) {
- if ($Config.Interval -ge $Config.FirstInterval -and $Config.Interval -ge $Config.StatsInterval) { $Variables.TimeToSleep = $Config.Interval }
- else {
+ # if ($Config.Interval -ge $Config.FirstInterval -and $Config.Interval -ge $Config.StatsInterval) { $Variables.TimeToSleep = $Config.Interval }
+ # else {
if ($CurrentMinerHashrate_Gathered -eq $true) { $Variables.TimeToSleep = $Config.FirstInterval }
else { $Variables.TimeToSleep = $Config.StatsInterval }
- }
+ # }
} else {
$Variables.TimeToSleep = $Config.Interval
}
"--------------------------------------------------------------------------------" | out-host
#Do nothing for a few seconds as to not overload the APIs
if ($newMiner -eq $true) {
- if ($Config.Interval -ge $Config.FirstInterval -and $Config.Interval -ge $Config.StatsInterval) { $Variables.TimeToSleep = $Config.Interval }
- else {
+ # if ($Config.Interval -ge $Config.FirstInterval -and $Config.Interval -ge $Config.StatsInterval) { $Variables.TimeToSleep = $Config.Interval }
+ # else {
if ($CurrentMinerHashrate_Gathered -eq $true) { $Variables.TimeToSleep = $Config.FirstInterval }
else { $Variables.TimeToSleep = $Config.StatsInterval }
- }
+ # }
} else {
$Variables.TimeToSleep = $Config.Interval
}
# Prevent switching during donation
- If ( $Variables.DonationRunning ) { If ($Config.Interval -ge ($Config.Donate * 60)) {$Variables.TimeToSleep = $Config.Interval} else {$Variables.TimeToSleep = $Config.Donate * 60 }}
+ # If ( $Variables.DonationRunning ) { If ($Config.Interval -ge ($Config.Donate * 60)) {$Variables.TimeToSleep = $Config.Interval} else {$Variables.TimeToSleep = $Config.Donate * 60 }}
#Save current hash rates
- $Variables.ActiveMinerPrograms | ForEach {
+ $Variables["ActiveMinerPrograms"] | ForEach {
if($_.Process -eq $null -or $_.Process.HasExited)
{
if($_.Status -eq "Running"){$_.Status = "Failed";$_.FailedCount++}
@@ -684,15 +786,15 @@ $CycleTime = Measure-Command -Expression {
# }
<#
- For some reason (need to investigate) $Variables.ActiveMinerPrograms.psobject.TypeNames
+ For some reason (need to investigate) $Variables["ActiveMinerPrograms"].psobject.TypeNames
Inflates adding several lines at each loop and causing a memory leak after log runtime
Code below copies the object which results in a new version which avoid the problem.
Will need rework.
#>
- $Variables.ActiveMinerPrograms | Where {$_.Status -ne "Running"} | foreach {$_.process = $_.process | select HasExited,StartTime,ExitTime}
- $ActiveMinerProgramsCOPY = @()
- $Variables.ActiveMinerPrograms | %{$ActiveMinerCOPY = [PSCustomObject]@{}; $_.psobject.properties | sort Name | %{$ActiveMinerCOPY | Add-Member -Force @{$_.Name = $_.Value}};$ActiveMinerProgramsCOPY += $ActiveMinerCOPY}
- $Variables.ActiveMinerPrograms = $ActiveMinerProgramsCOPY
+ $Variables["ActiveMinerPrograms"] | Where {$_.Status -ne "Running"} | foreach {$_.process = $_.process | select HasExited,StartTime,ExitTime}
+ $ActiveMinerProgramsCOPY = [System.Collections.ArrayList]::Synchronized(@())
+ $Variables["ActiveMinerPrograms"] | %{$ActiveMinerCOPY = [PSCustomObject]@{}; $_.psobject.properties | sort Name | %{$ActiveMinerCOPY | Add-Member -Force @{$_.Name = $_.Value}};$ActiveMinerProgramsCOPY += $ActiveMinerCOPY}
+ $Variables["ActiveMinerPrograms"] = $ActiveMinerProgramsCOPY
rv ActiveMinerProgramsCOPY
rv ActiveMinerCOPY
@@ -714,21 +816,25 @@ $CycleTime = Measure-Command -Expression {
# Mostly used for debug. Will execute code found in .\EndLoopCode.ps1 if exists.
if (Test-Path ".\EndLoopCode.ps1"){Invoke-Expression (Get-Content ".\EndLoopCode.ps1" -Raw)}
}
+
+ $CycleTime = Measure-Command -Expression $CycleScriptBlock.Ast.GetScriptBlock()
+
# $Variables.StatusText = "Cycle Time (seconds): $($CycleTime.TotalSeconds)"
- "Cycle Time (seconds): $($CycleTime.TotalSeconds)" | out-host
+ "$((Get-Date).ToString()) - Cycle Time (seconds): $($CycleTime.TotalSeconds)" | out-host
If ($variables.DonationRunning) {
- $Variables.StatusText = "Waiting $($Variables.TimeToSleep) seconds... | Next refresh: $((Get-Date).AddSeconds($Variables.TimeToSleep)) | Donation cycle. Thanks for your support!"
+ $Variables.StatusText = "Waiting $($Variables.TimeToSleep) seconds... | Next refresh: $((Get-Date).AddSeconds($Variables.TimeToSleep)) | Donation running. Thanks for your support!"
} else {
$Variables.StatusText = "Waiting $($Variables.TimeToSleep) seconds... | Next refresh: $((Get-Date).AddSeconds($Variables.TimeToSleep))"
+ $Variables.StatusText = "!! Check out the new server features and remote management web interface !!"
}
- $Variables | Add-Member -Force @{EndLoop = $True}
+ $Variables.EndLoop = $True
# Sleep $Variables.TimeToSleep
# }
# $Variables.BrainJobs | foreach { $_ | stop-job | remove-job }
# $Variables.BrainJobs = @()
# $pid | out-host
# $Variables | convertto-json | out-file ".\logs\variables.json"
-Remove-Variable Stats,Miners_Type_Combos,Miners_Index_Combos,Miners_Device_Combos,BestMiners_Combos,BestMiners_Combos_Comparison,AllPools,Pools,Miner_Pools,Miner_Pools_Comparison,Miner_Profits,Miner_Profits_Comparison,Miner_Profits_Bias,Miner
+# Remove-Variable Stats,Miners_Type_Combos,Miners_Index_Combos,Miners_Device_Combos,BestMiners_Combos,BestMiners_Combos_Comparison,AllPools,Pools,Miner_Pools,Miner_Pools_Comparison,Miner_Profits,Miner_Profits_Comparison,Miner_Profits_Bias,Miner
# Get-Variable | out-file ".\logs\variables.txt"
# $StackTrace | convertto-json | out-file ".\logs\stacktrace.json"
# remove-variable variables
diff --git a/Includes/EarningsTracker.ps1 b/Includes/EarningsTracker.ps1
new file mode 100644
index 0000000..ca919a1
--- /dev/null
+++ b/Includes/EarningsTracker.ps1
@@ -0,0 +1,301 @@
+<#
+This file is part of NPlusMiner
+Copyright (c) 2018-2019 MrPlus
+
+NPlusMiner is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+NPlusMiner is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+#>
+
+<#
+Product: NPlusMiner
+File: EarningsTrackerJob.ps1
+version: 5.4.1
+version date: 20190809
+#>
+Start-Transcript ".\logs\ET.log"
+$pwd | out-host
+
+# To start the job one could use the following
+# $job = Start-Job -FilePath .\EarningTrackerJob.ps1 -ArgumentList $params
+# Remove progress info from job.childjobs.Progress to avoid memory leak
+$ProgressPreference="SilentlyContinue"
+
+# Fix TLS version erroring
+[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
+
+# Set Process Priority
+(Get-Process -Id $PID).PriorityClass = "BelowNormal"
+
+# $args[0].GetEnumerator() | ForEach-Object { New-Variable -Name $_.Key -Value $_.Value }
+
+# If ($WorkingDirectory) {Set-Location $WorkingDirectory}
+# Start-Transcript ".\Logs\EarnTR.txt"
+If (Test-Path ".\logs\EarningTrackerData.json") {$AllBalanceObjectS = Get-Content ".\logs\EarningTrackerData.json" | ConvertFrom-JSON} else {$AllBalanceObjectS = @()}
+
+. .\Includes\include.ps1
+# $Config = Load-Config ".\Config\Config.json"
+ # If ($Config.Server_Client) {
+ # $ServerClientPasswd = ConvertTo-SecureString $Config.Server_ClientPassword -AsPlainText -Force
+ # $ServerClientCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_ClientUser, $ServerClientPasswd)
+ # $Variables = [hashtable]::Synchronized(@{})
+ # $Variables | Add-Member -Force @{ServerClientCreds = $ServerClientCreds}
+ # }
+$BalanceObjectS = @()
+$TrustLevel = 0
+$StartTime = Get-Date
+$LastAPIUpdateTime = Get-Date
+# $Variables | Add-Member @{EarningsObjects = [PSCustomObject]@{}}
+
+while ($true) {
+ if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1; RegisterLoaded(".\Includes\include.ps1")}
+
+ If ($Config.Server_Client) {
+ $Variables | Add-Member -Force @{ServerRunning = Try{ ((Invoke-WebRequest "http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/ping" -Credential $Variables.ServerClientCreds -TimeoutSec 3).content -eq "Server Alive")} Catch {$False} }
+ }
+
+# Set decimal separator so CSV files look good.
+ [System.Threading.Thread]::CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator = "."
+ [System.Threading.Thread]::CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator = "."
+
+#Read Config (ie. Pools to track)
+ $EarningsTrackerConfig = Get-content ".\config\EarningTrackerConfig.json" | ConvertFrom-JSON
+ $Interval = $EarningsTrackerConfig.PollInterval
+
+#Filter pools variants
+ $TrackPools = (($EarningsTrackerConfig.pools | sort -Unique).replace("plus","")).replace("24hr","")
+
+# Get pools api ref
+ If (-not $poolapi -or ($LastAPIUpdateTime -le (Get-Date).AddDays(-1))){
+ try {
+ $poolapi = Invoke-ProxiedWebRequest "http://tiny.cc/l355qy" | ConvertFrom-Json} catch {$poolapi = Get-content ".\Config\poolapiref.json" | Convertfrom-json}
+ $LastAPIUpdateTime = Get-Date
+ } else {
+ $poolapi = Get-content ".\Config\poolapiref.json" | Convertfrom-json
+ }
+
+#For each pool in config
+#Go loop
+ If (-not $Variables.DonationRunning) {
+ foreach ($Pool in $TrackPools) {
+ if ($poolapi -ne $null) {
+ $poolapi | ConvertTo-json | Out-File ".\Config\poolapiref.json"
+ If (($poolapi | ? {$_.Name -eq $pool}).EarnTrackSupport -eq "yes") {
+ $APIUri = ($poolapi | ? {$_.Name -eq $pool}).WalletUri
+ $PaymentThreshold = ($poolapi | ? {$_.Name -eq $pool}).PaymentThreshold
+ $BalanceJson = ($poolapi | ? {$_.Name -eq $pool}).Balance
+ $TotalJson = ($poolapi | ? {$_.Name -eq $pool}).Total
+
+ $ConfName = if ($PoolsConfig.$Pool -ne $Null){$Pool}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+ $Wallet =
+ if($Pool -eq "miningpoolhub"){
+ $PoolConf.APIKey
+ } else {
+ $PoolConf.Wallet
+ }
+
+ $CurDate = Get-Date
+ # Write-host $Pool
+ Write-Host "$($APIUri)$($Wallet)"
+ If ($Pool -eq "nicehash-V1"){
+ try {
+ $TempBalanceData = Invoke-ProxiedWebRequest ("$($APIUri)$($Wallet)") | ConvertFrom-Json } catch { }
+ if (-not $TempBalanceData.$BalanceJson) {$TempBalanceData | Add-Member -NotePropertyName $BalanceJson -NotePropertyValue ($TempBalanceData.result.Stats | measure -sum $BalanceJson).sum -Force}
+ if (-not $TempBalanceData.$TotalJson) {$TempBalanceData | Add-Member -NotePropertyName $TotalJson -NotePropertyValue ($TempBalanceData.result.Stats | measure -sum $BalanceJson).sum -Force}
+ } elseif ($Pool -eq "nicehash") {
+ try {
+ $TempBalanceData = Invoke-ProxiedWebRequest ("$($APIUri)$($Wallet)/rigs") | ConvertFrom-Json
+ [Double]$NHTotalBalance = [Double]($TempBalanceData.unpaidAmount) + [Double]($TempBalanceData.externalBalance)
+ $TempBalanceData | Add-Member -NotePropertyName $BalanceJson -NotePropertyValue $NHTotalBalance -Force
+ $TempBalanceData | Add-Member -NotePropertyName $TotalJson -NotePropertyValue $NHTotalBalance -Force
+ } catch { }
+ } elseif ($Pool -eq "miningpoolhub") {
+ try {
+ $TempBalanceData = ((((Invoke-ProxiedWebRequest ("$($APIUri)$($Wallet)")).content | ConvertFrom-Json).getuserallbalances).data | Where {$_.coin -eq "bitcoin"}) } catch { }#.confirmed
+ } else {
+ try {
+ $TempBalanceData = Invoke-ProxiedWebRequest ("$($APIUri)$($Wallet)") | ConvertFrom-Json } catch { }
+ }
+
+ If ($TempBalanceData.$TotalJson -gt 0){
+ $BalanceData = $TempBalanceData
+ $AllBalanceObjectS += [PSCustomObject]@{
+ Pool = $Pool
+ Date = $CurDate
+ balance = $BalanceData.$BalanceJson
+ unsold = $BalanceData.unsold
+ total_unpaid = $BalanceData.total_unpaid
+ total_paid = $BalanceData.total_paid
+ total_earned = $BalanceData.$TotalJson
+ currency = $BalanceData.currency
+ }
+ $BalanceObjectS = $AllBalanceObjectS | ? {$_.Pool -eq $Pool}
+ $BalanceObject = $BalanceObjectS[$BalanceOjectS.Count-1]
+ If ((($CurDate - ($BalanceObjectS[0].Date)).TotalMinutes) -eq 0) {$CurDate = $CurDate.AddMinutes(1)}
+
+
+
+ If ((($CurDate - ($BalanceObjectS[0].Date)).TotalDays) -ge 1) {
+ $Growth1 = $BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date -ge $CurDate.AddHours(-1)}).total_earned | measure -Minimum).Minimum
+ $Growth6 = $BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date -ge $CurDate.AddHours(-6)}).total_earned | measure -Minimum).Minimum
+ $Growth24 = $BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date -ge $CurDate.AddDays(-1)}).total_earned | measure -Minimum).Minimum
+ }
+ If ((($CurDate - ($BalanceObjectS[0].Date)).TotalDays) -lt 1) {
+ $Growth1 = $BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date -ge $CurDate.AddHours(-1)}).total_earned | measure -Minimum).Minimum
+ $Growth6 = $BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date -ge $CurDate.AddHours(-6)}).total_earned | measure -Minimum).Minimum
+ $Growth24 = (($BalanceObject.total_earned - $BalanceObjectS[0].total_earned) / ($CurDate - ($BalanceObjectS[0].Date)).TotalHours)*24
+ }
+ If ((($CurDate - ($BalanceObjectS[0].Date)).TotalHours) -lt 6) {
+ $Growth1 = $BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date -ge $CurDate.AddHours(-1)}).total_earned | measure -Minimum).Minimum
+ $Growth6 = (($BalanceObject.total_earned - $BalanceObjectS[0].total_earned) / ($CurDate - ($BalanceObjectS[0].Date)).TotalHours)*6
+ }
+ If ((($CurDate - ($BalanceObjectS[0].Date)).TotalHours) -lt 1) {
+ $Growth1 = (($BalanceObject.total_earned - $BalanceObjectS[0].total_earned) / ($CurDate - ($BalanceObjectS[0].Date)).TotalMinutes)*60
+ }
+
+ $AvgBTCHour = If ((($CurDate - ($BalanceObjectS[0].Date)).TotalHours) -ge 1) {(($BalanceObject.total_earned - $BalanceObjectS[0].total_earned) / ($CurDate - ($BalanceObjectS[0].Date)).TotalHours)} else {$Growth1}
+
+ $EarningsObject = [PSCustomObject]@{
+ Pool = $pool
+ Wallet = $Wallet
+ Date = $CurDate
+ StartTime = $BalanceObjectS[0].Date
+ balance = $BalanceObject.balance
+ unsold = $BalanceObject.unsold
+ total_unpaid = $BalanceObject.total_unpaid
+ total_paid = $BalanceObject.total_paid
+ total_earned = $BalanceObject.total_earned
+ currency = $BalanceObject.currency
+ GrowthSinceStart = $BalanceObject.total_earned - $BalanceObjectS[0].total_earned
+ Growth1 = $Growth1
+ Growth6 = $Growth6
+ Growth24 = $Growth24
+ AvgHourlyGrowth = $AvgBTCHour
+ BTCD = $AvgBTCHour*24
+ EstimatedEndDayGrowth = If ((($CurDate - ($BalanceObjectS[0].Date)).TotalHours) -ge 1) {($AvgBTCHour * ((Get-Date -Hour 0 -Minute 00 -Second 00).AddDays(1).AddSeconds(-1) - $CurDate).Hours)} else {$Growth1 * ((Get-Date -Hour 0 -Minute 00 -Second 00).AddDays(1).AddSeconds(-1) - $CurDate).Hours}
+ EstimatedPayDate = if ($PaymentThreshold){IF ($BalanceObject.balance -lt $PaymentThreshold) {If ($AvgBTCHour -gt 0.0000000000000001) {$CurDate.AddHours(($PaymentThreshold - $BalanceObject.balance) / ($AvgBTCHour))} Else {"Unknown"}} else {"Next Payout !"}}else{"Unknown"}
+ TrustLevel = if(($CurDate - ($BalanceObjectS[0].Date)).TotalMinutes -le 360){($CurDate - ($BalanceObjectS[0].Date)).TotalMinutes/360}else{1}
+ PaymentThreshold = $PaymentThreshold
+ TotalHours = ($CurDate - ($BalanceObjectS[0].Date)).TotalHours
+ }
+ # "$($EarningsObject.Pool): $($EarningsObject.balance)" | out-host
+
+ if (!$Variables.Earnings) {$Variables | Add-Member -Force @{Earnings = @()}}
+ $EarningsTemp = @($Variables.Earnings | ? {$_.Pool -ne $Pool}).Clone()
+ $EarningsTemp += $EarningsObject
+ $Variables.Earnings = $EarningsTemp.Clone()
+
+ if ($EarningsTrackerConfig.EnableLog){$EarningsObject | Export-Csv -NoTypeInformation -Append ".\Logs\EarningTrackerLog.csv"}
+
+ If (Test-Path ".\Logs\DailyEarnings.csv") {
+ $DailyEarnings = Import-Csv ".\Logs\DailyEarnings.csv" # Add filter on mw # days from config.
+ If ($DailyEarnings | ? {$_.Date -eq $CurDate.ToString("MM/dd/yyyy") -and $_.Pool -eq $Pool}) {
+ $DailyEarnings | select Date,Pool,
+ @{Name="DailyEarnings";Expression={
+ If ($_.Date -eq ($CurDate.ToString("MM/dd/yyyy")) -and $_.Pool -eq $Pool) {
+ If ($_.PrePaimentDayValue -gt 0) {
+ #Paiment occured
+ ($_.PrePaimentDayValue - $_.FirstDayValue) + ($BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date.DayOfYear -eq $CurDate.DayOfYear}).total_earned | measure -minimum).minimum)
+ } else {
+ $BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date.DayOfYear -eq $CurDate.DayOfYear}).total_earned | measure -minimum).minimum
+ }
+ } else {$_.DailyEarnings}
+ }},
+ FirstDayDate,
+ FirstDayValue,
+ @{Name="LastDayDate";Expression={
+ If ($_.Date -eq ($CurDate.ToString("MM/dd/yyyy")) -and $_.Pool -eq $Pool) {
+ $BalanceObject.Date
+ } else {$_.LastDayDate}
+ }},
+ @{Name="LastDayValue";Expression={
+ If ($_.Date -eq ($CurDate.ToString("MM/dd/yyyy")) -and $_.Pool -eq $Pool) {
+ $BalanceObject.total_earned
+ } else {$_.LastDayValue}
+ }},
+ @{Name="PrePaimentDayValue";Expression={
+ If (($_.Date -eq ($CurDate.ToString("MM/dd/yyyy")) -and $_.Pool -eq $Pool) -and ($BalanceObject.total_earned -lt ($BalanceObjectS[$BalanceObjectS.Count-2].total_earned/2))) {
+ $BalanceObjectS[$BalanceObjectS.Count-2].total_earned
+ } else {$_.PrePaimentDayValue}
+ }},
+ @{Name="Balance";Expression={
+ If ($_.Date -eq ($CurDate.ToString("MM/dd/yyyy")) -and $_.Pool -eq $Pool) {
+ $BalanceObject.balance
+ } else {$_.Balance}
+ }},
+ @{Name="BTCD";Expression={
+ If ($_.Date -eq ($CurDate.ToString("MM/dd/yyyy")) -and $_.Pool -eq $Pool) {
+ $BalanceObject.Growth24
+ } else {$_.BTCD}
+ }} | Export-Csv ".\Logs\DailyEarnings.csv" -NoTypeInformation
+ } else {
+ $DailyEarnings = [PSCustomObject]@{
+ Date = $CurDate.ToString("MM/dd/yyyy")
+ Pool = $Pool
+ DailyEarnings = $BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date.DayOfYear -eq $CurDate.DayOfYear}).total_earned | measure -minimum).minimum
+ FirstDayDate = $BalanceObject.Date
+ FirstDayValue = $BalanceObject.total_earned
+ LastDayDate = $BalanceObject.Date
+ LastDayValue = $BalanceObject.total_earned
+ PrePaimentDayValue = 0
+ Balance = $BalanceObject.Balance
+ BTCD = $BalanceObject.Growth24
+ }
+ $DailyEarnings | Export-Csv ".\Logs\DailyEarnings.csv" -NoTypeInformation -Append
+ }
+
+ } else {
+ $DailyEarnings = [PSCustomObject]@{
+ Date = $CurDate.ToString("MM/dd/yyyy")
+ Pool = $Pool
+ DailyEarnings = $BalanceObject.total_earned - (($BalanceObjectS | ? {$_.Date.DayOfYear -eq $CurDate.DayOfYear}).total_earned | measure -minimum).minimum
+ FirstDayDate = $BalanceObject.Date
+ FirstDayValue = $BalanceObject.total_earned
+ LastDayDate = $BalanceObject.Date
+ LastDayValue = $BalanceObject.total_earned
+ PrePaimentDayValue = 0
+ Balance = $BalanceObject.Balance
+ BTCD = $BalanceObject.Growth24
+ }
+ $DailyEarnings | Export-Csv ".\Logs\DailyEarnings.csv" -NoTypeInformation
+ }
+ rv DailyEarnings
+
+ # Some pools do reset "Total" after payment (zpool)
+ # Results in showing bad negative earnings
+ # Detecting if current is more than 50% less than previous and reset history if so
+ If ($BalanceObject.total_earned -lt ($BalanceObjectS[$BalanceObjectS.Count-2].total_earned/2)){$AllBalanceObjectS=$AllBalanceObjectS | ? {$_.Pool -ne $Pool};$AllBalanceObjectS += $BalanceObject}
+ rv TempBalanceData
+ } #else {$Pool | Out-Host} #else {return}
+ }
+ }
+ }
+ }
+
+ If ($AllBalanceObjectS.Count -gt 1) {$AllBalanceObjectS = $AllBalanceObjectS | ? {$_.Date -ge $CurDate.AddDays(-1).AddHours(-1)}}
+ # Save data only at defined interval. Limit disk access
+ If ((Get-Date) -gt $WriteAt) {
+ $WriteAt = (Get-Date).AddMinutes($EarningsTrackerConfig.WriteEvery)
+ if ($AllBalanceObjectS.Count -gt 1) {$AllBalanceObjectS | ConvertTo-JSON | Out-File ".\logs\EarningTrackerData.json"}
+ }
+
+
+ # Sleep until next update based on $Interval. Modulo $Interval.
+ # Sleep (60*($Interval-((get-date).minute%$Interval))) # Changed to avoid pool API load.
+ If (($EarningsObject.Date - $EarningsObject.StartTime).TotalMinutes -le 20){
+ Sleep (60*($Interval/2))
+ }else{
+ Sleep (60*($Interval))
+ }
+}
diff --git a/Includes/EarningsTrackerJob.ps1 b/Includes/EarningsTrackerJob.ps1
index ad2d50d..0841ef5 100644
--- a/Includes/EarningsTrackerJob.ps1
+++ b/Includes/EarningsTrackerJob.ps1
@@ -40,12 +40,27 @@ If ($WorkingDirectory) {Set-Location $WorkingDirectory}
# Start-Transcript ".\Logs\EarnTR.txt"
If (Test-Path ".\logs\EarningTrackerData.json") {$AllBalanceObjectS = Get-Content ".\logs\EarningTrackerData.json" | ConvertFrom-JSON} else {$AllBalanceObjectS = @()}
+. .\Includes\include.ps1
+$Config = Load-Config ".\Config\Config.json"
+ If ($Config.Server_Client) {
+ $ServerClientPasswd = ConvertTo-SecureString $Config.Server_ClientPassword -AsPlainText -Force
+ $ServerClientCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_ClientUser, $ServerClientPasswd)
+ $Variables = [hashtable]::Synchronized(@{})
+ $Variables | Add-Member -Force @{ServerClientCreds = $ServerClientCreds}
+ }
$BalanceObjectS = @()
$TrustLevel = 0
$StartTime = Get-Date
$LastAPIUpdateTime = Get-Date
while ($true) {
+ If ($Config.Server_Client) {
+ $Variables | Add-Member -Force @{ServerRunning = Try{ ((Invoke-WebRequest "http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/ping" -Credential $Variables.ServerClientCreds -TimeoutSec 3).content -eq "Server Alive")} Catch {$False} }
+ }
+
+# Set decimal separator so CSV files look good.
+ [System.Threading.Thread]::CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator = "."
+ [System.Threading.Thread]::CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator = "."
#Read Config (ie. Pools to track)
$EarningsTrackerConfig = Get-content ".\config\EarningTrackerConfig.json" | ConvertFrom-JSON
@@ -55,13 +70,13 @@ while ($true) {
$TrackPools = (($EarningsTrackerConfig.pools | sort -Unique).replace("plus","")).replace("24hr","")
# Get pools api ref
- If (-not $poolapi -or ($LastAPIUpdateTime -le (Get-Date).AddDays(-1))){
- try {
- $poolapi = Invoke-WebRequest "http://tiny.cc/l355qy" -TimeoutSec 15 -UseBasicParsing -Headers @{"Cache-Control"="no-cache"} | ConvertFrom-Json} catch {$poolapi = Get-content ".\Config\poolapiref.json" | Convertfrom-json}
- $LastAPIUpdateTime = Get-Date
- } else {
- $poolapi = Get-content ".\Config\poolapiref.json" | Convertfrom-json
- }
+ If (-not $poolapi -or ($LastAPIUpdateTime -le (Get-Date).AddDays(-1))){
+ try {
+ $poolapi = Invoke-ProxiedWebRequest "http://tiny.cc/l355qy" | ConvertFrom-Json} catch {$poolapi = Get-content ".\Config\poolapiref.json" | Convertfrom-json}
+ $LastAPIUpdateTime = Get-Date
+ } else {
+ $poolapi = Get-content ".\Config\poolapiref.json" | Convertfrom-json
+ }
#For each pool in config
#Go loop
@@ -78,43 +93,49 @@ while ($true) {
$PoolConf = $PoolsConfig.$ConfName
$Wallet =
- if($Pool -eq "miningpoolhub"){
+ if($Pool -in @("miningpoolhub","prohashing")){
$PoolConf.APIKey
} else {
$PoolConf.Wallet
}
$CurDate = Get-Date
- # Write-host $Pool
- # Write-Host "$($APIUri)$($Wallet)"
+ # Write-host $Pool
+ # Write-Host "$($APIUri)$($Wallet)"
If ($Pool -eq "nicehash-V1"){
try {
- $TempBalanceData = Invoke-WebRequest ("$($APIUri)$($Wallet)") -TimeoutSec 15 -UseBasicParsing -Headers @{"Cache-Control"="no-cache"} | ConvertFrom-Json } catch { }
+ $TempBalanceData = Invoke-ProxiedWebRequest ("$($APIUri)$($Wallet)") -UseBasicParsing | ConvertFrom-Json } catch { }
if (-not $TempBalanceData.$BalanceJson) {$TempBalanceData | Add-Member -NotePropertyName $BalanceJson -NotePropertyValue ($TempBalanceData.result.Stats | measure -sum $BalanceJson).sum -Force}
if (-not $TempBalanceData.$TotalJson) {$TempBalanceData | Add-Member -NotePropertyName $TotalJson -NotePropertyValue ($TempBalanceData.result.Stats | measure -sum $BalanceJson).sum -Force}
} elseif ($Pool -eq "nicehash") {
try {
- $TempBalanceData = Invoke-WebRequest ("$($APIUri)$($Wallet)/rigs") -TimeoutSec 15 -UseBasicParsing -Headers @{"Cache-Control"="no-cache"} | ConvertFrom-Json } catch { }
+ $TempBalanceData = Invoke-ProxiedWebRequest ("$($APIUri)$($Wallet)/rigs2") -UseBasicParsing | ConvertFrom-Json } catch { }
[Double]$NHTotalBalance = [Double]($TempBalanceData.unpaidAmount) + [Double]($TempBalanceData.externalBalance)
$TempBalanceData | Add-Member -NotePropertyName $BalanceJson -NotePropertyValue $NHTotalBalance -Force
$TempBalanceData | Add-Member -NotePropertyName $TotalJson -NotePropertyValue $NHTotalBalance -Force
+ $TempBalanceData | Add-Member -NotePropertyName "currency" -NotePropertyValue "BTC" -Force
} elseif ($Pool -eq "miningpoolhub") {
try {
- $TempBalanceData = ((((Invoke-WebRequest ("$($APIUri)$($Wallet)") -TimeoutSec 15 -UseBasicParsing -Headers @{"Cache-Control"="no-cache"}).content | ConvertFrom-Json).getuserallbalances).data | Where {$_.coin -eq "bitcoin"}) } catch { }#.confirmed
+ $TempBalanceData = ((((Invoke-ProxiedWebRequest ("$($APIUri)$($Wallet)") -UseBasicParsing).content | ConvertFrom-Json).getuserallbalances).data | Where {$_.coin -eq "bitcoin"}) } catch { }#.confirmed
+ $TempBalanceData | Add-Member -NotePropertyName "currency" -NotePropertyValue "BTC" -Force
+ } elseif ($Pool -eq "prohashing") {
+ try {
+ $TempBalanceData = (((Invoke-ProxiedWebRequest ("$($APIUri)$($Wallet)") -UseBasicParsing).content | ConvertFrom-Json).data.balances.($Config.Passwordcurrency)) } catch { }#.confirmed
+ $TempBalanceData | Add-Member -NotePropertyName "currency" -NotePropertyValue $Config.Passwordcurrency -Force
} else {
try {
- $TempBalanceData = Invoke-WebRequest ("$($APIUri)$($Wallet)") -TimeoutSec 15 -UseBasicParsing -Headers @{"Cache-Control"="no-cache"} | ConvertFrom-Json } catch { }
+ $TempBalanceData = Invoke-ProxiedWebRequest ("$($APIUri)$($Wallet)") -UseBasicParsing | ConvertFrom-Json } catch { }
}
If ($TempBalanceData.$TotalJson -gt 0){
$BalanceData = $TempBalanceData
$AllBalanceObjectS += [PSCustomObject]@{
Pool = $Pool
Date = $CurDate
- balance = $BalanceData.$BalanceJson
- unsold = $BalanceData.unsold
- total_unpaid = $BalanceData.total_unpaid
- total_paid = $BalanceData.total_paid
- total_earned = $BalanceData.$TotalJson
+ balance = [Math]::Round($BalanceData.$BalanceJson, 8)
+ unsold = [Math]::Round($BalanceData.unsold, 8)
+ total_unpaid = [Math]::Round($BalanceData.total_unpaid, 8)
+ total_paid = [Math]::Round($BalanceData.total_paid, 8)
+ total_earned = [Math]::Round($BalanceData.$TotalJson, 8)
currency = $BalanceData.currency
}
$BalanceObjectS = $AllBalanceObjectS | ? {$_.Pool -eq $Pool}
@@ -160,12 +181,12 @@ while ($true) {
AvgHourlyGrowth = $AvgBTCHour
BTCD = $AvgBTCHour*24
EstimatedEndDayGrowth = If ((($CurDate - ($BalanceObjectS[0].Date)).TotalHours) -ge 1) {($AvgBTCHour * ((Get-Date -Hour 0 -Minute 00 -Second 00).AddDays(1).AddSeconds(-1) - $CurDate).Hours)} else {$Growth1 * ((Get-Date -Hour 0 -Minute 00 -Second 00).AddDays(1).AddSeconds(-1) - $CurDate).Hours}
- EstimatedPayDate = if ($PaymentThreshold){IF ($BalanceObject.balance -lt $PaymentThreshold) {If ($AvgBTCHour -gt 0) {$CurDate.AddHours(($PaymentThreshold - $BalanceObject.balance) / $AvgBTCHour)} Else {"Unknown"}} else {"Next Payout !"}}else{"Unknown"}
+ EstimatedPayDate = if ($PaymentThreshold){IF ($BalanceObject.balance -lt $PaymentThreshold) {If ($AvgBTCHour -gt 0.0000000000000001) {$CurDate.AddHours(($PaymentThreshold - $BalanceObject.balance) / ($AvgBTCHour))} Else {"Unknown"}} else {"Next Payout !"}}else{"Unknown"}
TrustLevel = if(($CurDate - ($BalanceObjectS[0].Date)).TotalMinutes -le 360){($CurDate - ($BalanceObjectS[0].Date)).TotalMinutes/360}else{1}
PaymentThreshold = $PaymentThreshold
TotalHours = ($CurDate - ($BalanceObjectS[0].Date)).TotalHours
}
-
+
$EarningsObject
if ($EarningsTrackerConfig.EnableLog){$EarningsObject | Export-Csv -NoTypeInformation -Append ".\Logs\EarningTrackerLog.csv"}
@@ -268,5 +289,4 @@ while ($true) {
}else{
Sleep (60*($Interval))
}
-
}
diff --git a/Includes/Include.ps1 b/Includes/Include.ps1
index 6e25397..eeba238 100644
--- a/Includes/Include.ps1
+++ b/Includes/Include.ps1
@@ -46,7 +46,7 @@ function Get-MemoryUsage {
If (!$Variables.PerfCounterProcPath) {
$proc_path=((Get-Counter "\Process(*)\ID Process").CounterSamples | ? {$_.RawValue -eq $pid}).Path
$proc_path = $proc_path -replace "id process","Working Set - Private"
- $Variables | Add-Member -Force @{PerfCounterProcPath = $proc_path}
+ $Variables.PerfCounterProcPath = $proc_path
}
# Write-Host -Object ('PagedMemorySize : {0:n1} MB ' -f ($P.PagedMemorySize/1024))
@@ -65,7 +65,7 @@ Function GetNVIDIADriverVersion {
Function Global:RegisterLoaded ($File) {
New-Item -Path function: -Name script:"$((Get-FileHash (Resolve-Path $File)).Hash)" -Value {$true} -EA SilentlyContinue | Add-Member @{"File" = (Resolve-Path $File).Path} -EA SilentlyContinue
- $Variables.StatusText = "File loaded - $($file) - $((Get-PSCallStack).Command[1])"
+ # $Variables.StatusText = "File loaded - $($file) - $((Get-PSCallStack).Command[1])"
}
Function Global:IsLoaded ($File) {
@@ -159,7 +159,7 @@ namespace PInvoke.Win32 {
Start-Sleep 1
}
} ) | Out-Null
- $Variables | Add-Member -Force @{IdleRunspaceHandle = $idlePowershell.BeginInvoke()}
+ $Variables.IdleRunspaceHandle = $idlePowershell.BeginInvoke()
}
Function Update-Monitoring {
@@ -172,11 +172,11 @@ Function Update-Monitoring {
If ($Config.ReportToServer) {
$Version = "$($Variables.CurrentProduct) $($Variables.CurrentVersion.ToString())"
$Status = If ($Variables.Paused) { "Paused" } else { "Running" }
- $RunningMiners = $Variables.ActiveMinerPrograms | Where-Object {$_.Status -eq "Running"}
+ $RunningMiners = $Variables.ActiveMinerPrograms.Where( {$_.Status -eq "Running"} )
# Add the associated object from $Variables.Miners since we need data from that too
$RunningMiners | Foreach-Object {
$RunningMiner = $_
- $Miner = $Variables.Miners | Where-Object {$_.Name -eq $RunningMiner.Name -and $_.Path -eq $RunningMiner.Path -and $_.Arguments -eq $RunningMiner.Arguments}
+ $Miner = $Variables.Miners.Where( {$_.Name -eq $RunningMiner.Name -and $_.Path -eq $RunningMiner.Path -and $_.Arguments -eq $RunningMiner.Arguments} )
$_ | Add-Member -Force @{'Miner' = $Miner}
}
@@ -238,8 +238,8 @@ Function Update-Monitoring {
}
}
- $Variables | Add-Member -Force @{Workers = $Workers}
- $Variables | Add-Member -Force @{WorkersLastUpdated = (Get-Date)}
+ $Variables.Workers = $Workers
+ $Variables.WorkersLastUpdated = (Get-Date)
}
Catch {
$Variables.StatusText = "Unable to retrieve worker data from $($Config.MonitoringServer)"
@@ -257,7 +257,7 @@ Function Start-Mining {
$CycleRunspace.SessionStateProxy.Path.SetLocation((Split-Path $script:MyInvocation.MyCommand.Path))
$Global:powershell = [powershell]::Create()
$powershell.Runspace = $CycleRunspace
- $powershell.AddScript( {
+ $scriptblock = {
#Start the log
Start-Transcript -Path ".\logs\CoreCyle-$((Get-Date).ToString('yyyyMMdd')).log" -Append -Force
# Purge Logs more than 10 days
@@ -276,17 +276,17 @@ Function Start-Mining {
# Keep updating exchange rate
$Rates = Invoke-RestMethod "https://api.coinbase.com/v2/exchange-rates?currency=BTC" -TimeoutSec 15 -UseBasicParsing | Select-Object -ExpandProperty data | Select-Object -ExpandProperty rates
- $Config.Currency | Where-Object {$Rates.$_} | ForEach-Object {$Rates | Add-Member $_ ([Double]$Rates.$_) -Force}
- $Variables | Add-Member -Force @{Rates = $Rates}
+ $Config.Currency.Where( {$Rates.$_} ) | ForEach-Object {$Rates | Add-Member $_ ([Double]$Rates.$_) -Force}
+ $Variables.Rates = $Rates
# Update the UI every 30 seconds, and the Last 1/6/24hr and text window every 2 minutes
for ($i = 0; $i -lt 4; $i++) {
if ($i -eq 3) {
- $Variables | Add-Member -Force @{EndLoop = $True}
+ $Variables.EndLoop = $True
Update-Monitoring
}
else {
- $Variables | Add-Member -Force @{EndLoop = $False}
+ $Variables.EndLoop = $False
}
$Variables.StatusText = "Mining paused"
@@ -299,9 +299,12 @@ Function Start-Mining {
Sleep $Variables.TimeToSleep
}
}
- }) | Out-Null
- $Variables | add-Member -Force @{CycleRunspaceHandle = $powershell.BeginInvoke()}
- $Variables | Add-Member -Force @{LastDonated = (Get-Date).AddDays(-1).AddHours(1)}
+ }
+ $powershell.AddScript( {. $args[0]} ) | Out-Null
+ $powershell.AddArgument($scriptblock.Ast.GetScriptBlock())
+
+ $Variables.CycleRunspaceHandle = $powershell.BeginInvoke()
+ $Variables.LastDonated = (Get-Date).AddDays(-1).AddHours(1)
}
Function Stop-Mining {
@@ -391,7 +394,23 @@ Function Load-Config {
}}
}
}
+
+ If ($Config.Server_Password -in @("","3890292d-990c-44ba-b779-552829fc0bc4")) {
+ $Guid = (New-Guid).Guid
+ $Config.Server_Password = $Guid
+ If ($Config.Server_ClientPassword -in @("","3890292d-990c-44ba-b779-552829fc0bc4")) {
+ $Config.Server_ClientPassword = $Guid
+ }
+ }
+ if ($Variables) {
+ $ServerPasswd = ConvertTo-SecureString $Config.Server_Password -AsPlainText -Force
+ $ServerCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_User, $ServerPasswd)
+ $Variables.ServerCreds = $ServerCreds
+ $ServerClientPasswd = ConvertTo-SecureString $Config.Server_ClientPassword -AsPlainText -Force
+ $ServerClientCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_ClientUser, $ServerClientPasswd)
+ $Variables.ServerClientCreds = $ServerClientCreds
+ }
$Config
}
}
@@ -405,6 +424,9 @@ Function Write-Config {
)
If ($Config.ManualConfig) {Update-Status("Manual config mode - Not saving config"); return}
If ($Config -ne $null) {
+ If (@($Config.Algorithm).Where({$_.StartsWith("+")})) { $Config | Add-Member -Force @{AlgoInclude = @(@($Config.Algorithm).Where({$_.StartsWith("+")}).substring(1) | ForEach {Get-Algorithm $_})} } else {$Config | Add-Member -Force @{AlgoInclude = @()}}
+ If (@($Config.Algorithm).Where({$_.StartsWith("-")})) { $Config | Add-Member -Force @{AlgoExclude = @(@($Config.Algorithm).Where({$_.StartsWith("-")}).substring(1) | ForEach {Get-Algorithm $_})} } else {$Config | Add-Member -Force @{AlgoExclude = @()}}
+
if (Test-Path $ConfigFile) {Copy-Item $ConfigFile "$($ConfigFile).backup"}
$OrderedConfig = [PSCustomObject]@{}; ($config | select -Property * -ExcludeProperty PoolsConfig) | % {$_.psobject.properties | sort Name | % {$OrderedConfig | Add-Member -Force @{$_.Name = $_.Value}}}
$OrderedConfig | ConvertTo-json | out-file $ConfigFile
@@ -574,13 +596,33 @@ function Get-ChildItemContent {
$PropertyKeys | ForEach-Object {
if ($Property.$_ -is [String]) {
$Property.$_ = Invoke-Expression "`"$($Property.$_)`""
- }
+ }
}
}
}
}
$ChildItems
}
+
+function Get-SubScriptContent {
+ param(
+ [Parameter(Mandatory = $true)]
+ [String]$Path,
+ [Parameter(Mandatory = $false)]
+ [Array]$Include = @()
+ )
+ [System.Collections.ArrayList]$Content = @()
+ $ChildItems = Get-ChildItem -Recurse -Path $Path -Include $Include | ForEach-Object {
+ $Name = $_.BaseName
+ $FileName = $_.Name
+ if ($_.Extension -eq ".ps1") {
+ &$_.FullName | ForEach-Object {$Content.Add([PSCustomObject]@{Name = $Name; Content = $_}) > $null }
+ # &$_.FullName | ForEach-Object {$Content += [PSCustomObject]@{Name = $Name; Content = $_} }
+ }
+ }
+ $Content
+}
+
function Invoke_TcpRequest {
param(
@@ -746,6 +788,13 @@ function Get-HashRate {
$Data = $Request | ConvertFrom-Json
$HashRate = [Double]($Data.devices.speed | Measure-Object -Sum).Sum
}
+ "gminerdual" {
+ $Message = @{id = 1; method = "getstat" } | ConvertTo-Json -Compress
+ $Request = Invoke_httpRequest $Server $Port "/stat" 5
+ $Data = $Request | ConvertFrom-Json
+ $HashRate = [Double]($Data.devices.speed2 | Measure-Object -Sum).Sum
+ $HashRate_Dual = [Double]($Data.devices.speed | Measure-Object -Sum).Sum
+ }
"claymore" {
$Request = Invoke_httpRequest $Server $Port "" 5
@@ -765,7 +814,7 @@ function Get-HashRate {
$HashRate = [int](($Data.result[2] -split ';')[0]) * 1000
}
}
-
+
"TTminer" {
$Parameters = @{id = 1; jsonrpc = "2.0"; method = "miner_getstat1"} | ConvertTo-Json -Compress
@@ -808,7 +857,7 @@ function Get-HashRate {
$Request = Invoke_TcpRequest $server $port $message 5
$Data = $Request | ConvertFrom-Json
$HashRate = [Double](($Data.result.speed_sps) | Measure-Object -Sum).Sum
- }
+ }
"wrapper" {
$HashRate = ""
@@ -858,11 +907,29 @@ function Get-HashRate {
}
}
+ "NBMinerdualEaglesong" {
+ $Request = Invoke_httpRequest $Server $Port "/api/v1/status" 5
+ if ($Request) {
+ $Data = $Request | ConvertFrom-Json
+ $HashRate = [double]$Data.miner.total_hashrate_raw
+ $HashRate_Dual = [double]$Data.miner.total_hashrate2_raw
+ }
+ }
+
+ "NBMinerdualHandshake" {
+ $Request = Invoke_httpRequest $Server $Port "/api/v1/status" 5
+ if ($Request) {
+ $Data = $Request | ConvertFrom-Json
+ $HashRate = [double]$Data.miner.total_hashrate2_raw
+ $HashRate_Dual = [double]$Data.miner.total_hashrate_raw
+ }
+ }
+
"LOL" {
$Request = Invoke_httpRequest $Server $Port "/summary" 5
if ($Request) {
$Data = $Request | ConvertFrom-Json
- $HashRate = [Double]$data.Session.Performance_Summary
+ $HashRate = [Double]$data.Session.Performance_Summary
}
}
@@ -870,7 +937,7 @@ function Get-HashRate {
$Request = Invoke_TcpRequest $Server $Port "status" 5
if ($Request) {
$Data = $Request | ConvertFrom-Json
- $HashRate = [Double]$Data.result.speed_ips * 1000000
+ $HashRate = [Double]$Data.result.speed_ips * 1000000
}
}
@@ -1079,38 +1146,38 @@ function Start-SubProcess {
$CreateProcessExitCode = [Kernel32]::CreateProcess($lpApplicationName, $lpCommandLine, [ref] $lpProcessAttributes, [ref] $lpThreadAttributes, $bInheritHandles, $dwCreationFlags, $lpEnvironment, $lpCurrentDirectory, [ref] $lpStartupInfo, [ref] $lpProcessInformation)
$x = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
- # Write-Host "CreateProcessExitCode: $CreateProcessExitCode"
+ # Write-Host "CreateProcessExitCode: $CreateProcessExitCode"
# Write-Host "Last error $x"
- Write-Host $lpCommandLine
- # Write-Host "lpProcessInformation.dwProcessID: $($lpProcessInformation.dwProcessID)"
-
- If ($CreateProcessExitCode) {
- # Write-Host "lpProcessInformation.dwProcessID - WHEN TRUE: $($lpProcessInformation.dwProcessID)"
-
- $Process = Get-Process -Id $lpProcessInformation.dwProcessID
-
- # Dirty workaround
- # Need to investigate. lpProcessInformation sometimes comes null even if process started
- # So getting process with the same FilePath if so
- $Tries = 0
- While ($Process -eq $null -and $Tries -le 5) {
- Write-Host "Can't get process - $Tries"
- $Tries++
- Sleep 1
- $Process = (Get-Process | ? {$_.Path -eq $FilePath})[0]
- Write-Host "Process= $($Process.Handle)"
- }
-
- if ($Process -eq $null) {
- Write-Host "Case 2 - Failed Get-Process"
- [PSCustomObject]@{ProcessId = $null}
- return
- }
- } else {
- Write-Host "Case 1 - Failed CreateProcess"
- [PSCustomObject]@{ProcessId = $null}
- return
- }
+ Write-Host $lpCommandLine
+ # Write-Host "lpProcessInformation.dwProcessID: $($lpProcessInformation.dwProcessID)"
+
+ If ($CreateProcessExitCode) {
+ # Write-Host "lpProcessInformation.dwProcessID - WHEN TRUE: $($lpProcessInformation.dwProcessID)"
+
+ $Process = Get-Process -Id $lpProcessInformation.dwProcessID
+
+ # Dirty workaround
+ # Need to investigate. lpProcessInformation sometimes comes null even if process started
+ # So getting process with the same FilePath if so
+ $Tries = 0
+ While ($Process -eq $null -and $Tries -le 5) {
+ Write-Host "Can't get process - $Tries"
+ $Tries++
+ Sleep 1
+ $Process = (Get-Process | ? {$_.Path -eq $FilePath})[0]
+ Write-Host "Process= $($Process.Handle)"
+ }
+
+ if ($Process -eq $null) {
+ Write-Host "Case 2 - Failed Get-Process"
+ [PSCustomObject]@{ProcessId = $null}
+ return
+ }
+ } else {
+ Write-Host "Case 1 - Failed CreateProcess"
+ [PSCustomObject]@{ProcessId = $null}
+ return
+ }
[PSCustomObject]@{ProcessId = $Process.Id; ProcessHandle = $Process.Handle}
@@ -1124,7 +1191,7 @@ function Start-SubProcess {
do {Start-Sleep 1; $JobOutput = Receive-Job $Job}
while ($JobOutput -eq $null)
- $Process = Get-Process | Where-Object Id -EQ $JobOutput.ProcessId
+ $Process = (Get-Process).Where( { $_.Id -EQ $JobOutput.ProcessId } )
$Process.Handle | Out-Null
$Process
}
@@ -1137,6 +1204,7 @@ function Expand-WebRequest {
[Parameter(Mandatory = $true)]
[String]$Path
)
+ [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
$FolderName_Old = ([IO.FileInfo](Split-Path $Uri -Leaf)).BaseName
$FolderName_New = Split-Path $Path -Leaf
@@ -1145,8 +1213,7 @@ function Expand-WebRequest {
if (Test-Path $FileName) {Remove-Item $FileName}
if (Test-Path "$(Split-Path $Path)\$FolderName_New") {Remove-Item "$(Split-Path $Path)\$FolderName_New" -Recurse -Force}
if (Test-Path "$(Split-Path $Path)\$FolderName_Old") {Remove-Item "$(Split-Path $Path)\$FolderName_Old" -Recurse -Force}
-
- Invoke-WebRequest $Uri -OutFile $FileName -TimeoutSec 15 -UseBasicParsing
+ Invoke-WebRequest -Uri $Uri -OutFile $FileName -TimeoutSec 15 -UseBasicParsing
Start-Process ".\Utils\7z" "x $FileName -o$(Split-Path $Path)\$FolderName_Old -y -spe" -Wait
if (Get-ChildItem "$(Split-Path $Path)\$FolderName_Old" | Where-Object PSIsContainer -EQ $false) {
Rename-Item "$(Split-Path $Path)\$FolderName_Old" "$FolderName_New"
@@ -1163,12 +1230,17 @@ function Get-Algorithm {
[Parameter(Mandatory = $true)]
[String]$Algorithm
)
+
+ If ((-not $Variables.Algorithms) -or (Get-Date).AddMinutes(-2) -ge $Variables.Algorithms.UpdatedCache ){
+ $Variables | Add-Member @{ Algorithms = Get-Content ".\Includes\Algorithms.txt" | ConvertFrom-Json } -Force
+ $Variables.Algorithms | Add-Member @{ UpdatedCache = get-date } -Force
+ }
- $Algorithms = Get-Content ".\Includes\Algorithms.txt" | ConvertFrom-Json
+ # $Algorithms = Get-Content ".\Includes\Algorithms.txt" | ConvertFrom-Json
$Algorithm = (Get-Culture).TextInfo.ToTitleCase(($Algorithm -replace "-", " " -replace "_", " ")) -replace " "
- if ($Algorithms.$Algorithm) {$Algorithms.$Algorithm}
+ if ($Variables.Algorithms.$Algorithm) {$Variables.Algorithms.$Algorithm}
else {$Algorithm}
}
@@ -1280,7 +1352,7 @@ Function Autoupdate {
ls .\OptionalMiners\ | ? {$_.name -notin (ls .\$UpdateFileName\OptionalMiners\).name} | % {Remove-Item -Recurse -Force $_.FullName}
# Update Optional Miners to Miners if in use
- ls .\OptionalMiners\ | ? {$_.name -in (ls .\Miners\).name} | % {Copy-Item -Force $_.FullName .\Miners\}
+ # ls .\OptionalMiners\ | ? {$_.name -in (ls .\Miners\).name} | % {Copy-Item -Force $_.FullName .\Miners\}
# Remove any obsolete miner file (ie. Not in new version Miners or OptionalMiners)
ls .\Miners\ | ? {$_.name -notin (ls .\$UpdateFileName\Miners\).name -and $_.name -notin (ls .\$UpdateFileName\OptionalMiners\).name} | % {Remove-Item -Recurse -Force $_.FullName}
@@ -1410,11 +1482,11 @@ Function Merge-Command {
Switch ($type) {
"Password" {
If ($Slave -and !($Slave.StartsWith(","))) {$Slave = ",$($Slave)"}
- ($Master.split(",") | ? {$_ -ne ""} | foreach {",$($_)"}) | foreach {
+ ($Master.split(",").Where({$_ -ne ""}) | foreach {",$($_)"}) | foreach {
$MasterPassArg = $_
- ($_.split("=") | ? {$_.startsWith(",")} | foreach {"$($_)="}) | Foreach {
+ ($_.split("=").Where({$_.startsWith(",")}) | foreach {"$($_)="}) | Foreach {
If ($_ -notin $NoReplacePassArgs) {
- If ($Slave -match "$_ *([^,]+)" | Out-Null) {
+ If ($Slave -match "$_ *([^,]+)") {
$Slave = $Slave -replace "$_ *([^,]+)",$MasterPassArg
} else {
$Slave = $Slave + $MasterPassArg
@@ -1425,22 +1497,31 @@ Function Merge-Command {
$Slave.Substring(1)
}
"Command" {
- If ($Master -and !$Master.StartsWith(" ")) {$Master = " $($Master)"}
- If ($Slave -and !$Slave.StartsWith(" ")) {$Slave = " $($Slave)"}
- If ($Master -and !$Master.EndsWith(" ")) {$Master = "$($Master) "}
- If ($Slave -and !$Slave.EndsWith(" ")) {$Slave = "$($Slave) "}
- ($Master.split(" ") | ? {$_.StartsWith("-")}) | Foreach {
+ ($Master.split(" ").Where({$_.StartsWith("-")})) | Foreach {
+ If ($Master -and !$Master.StartsWith(" ")) {$Master = " $($Master)"}
+ If ($Slave -and !$Slave.StartsWith(" ")) {$Slave = " $($Slave)"}
+ If ($Master -and !$Master.EndsWith(" ")) {$Master = "$($Master) "}
+ If ($Slave -and !$Slave.EndsWith(" ")) {$Slave = "$($Slave) "}
+ $Matches = $null
+ $MasterCmdArg = $null
If ($_ -notin $NoReplaceCmdArgs) {
if ($Master -match " $_ -") {
If (!($Slave -match " $_ -")) {
$Slave = $Slave + " $($_)"
}
- } elseif ($Master -match " $_ *([^-]+)") {
+ # } elseif ($Master -match " $_ *([^-]+)") {
+ # $MasterCmdArg = $Matches[0]
+ # If ($Slave -match " $_ *([^-]+)") {
+ # $Slave = $Slave -replace " $_ *([^-]+)",$MasterCmdArg
+ # } else {
+ # $Slave = $Slave + " $($MasterCmdArg)"
+ # }
+ } elseif ($Master -match " $_ *([^( -)])+") {
$MasterCmdArg = $Matches[0]
- If ($Slave -match " $_ *([^-]+)") {
- $Slave = $Slave -replace " $_ *([^-]+)",$MasterCmdArg
+ If ($Slave -match " $_ *([^( -)])+") {
+ $Slave = $Slave -replace " $_ *([^( -)])+",$MasterCmdArg
} else {
- $Slave = $Slave + " $($MasterCmdArg)"
+ $Slave = $Slave + " $($MasterCmdArg)"
}
} elseif ($Master -match " $_ *([^\s]+)") {
$MasterCmdArg = $Matches[0]
@@ -1449,10 +1530,10 @@ Function Merge-Command {
} else {
$Slave = $Slave + " $($MasterCmdArg)"
}
- } elseif ($Master -match " $_*([^\s]+)") {
+ } elseif ($Master -match " $_ ") {
$MasterCmdArg = $Matches[0]
- If ($Slave -match " $_*([^\s]+)") {
- $Slave = $Slave -replace " $_*([^\s]+)",$MasterCmdArg
+ If ($Slave -match " $_ ") {
+ $Slave = $Slave -replace " $_ ",$MasterCmdArg
} else {
$Slave = $Slave + " $($MasterCmdArg)"
}
@@ -1468,4 +1549,166 @@ Function Merge-Command {
}
}
+Function Invoke-ProxiedWebRequest {
+ $Request = $null
+ If ($Config.Server_Client -and $Variables.ServerRunning -and -not $ByPassServer -and -not $OutFile) {
+ Try {
+ $ProxyURi = "http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/Proxy/?url=$($Args[0])"
+ $Args[0] = $ProxyURi
+ # $Request = Invoke-WebRequest $ProxyURi -Credential $Variables.ServerClientCreds -TimeoutSec $TimeoutSec -UseBasicParsing -Headers $Headers
+ $Request = Invoke-WebRequest @Args -Credential $Variables.ServerClientCreds
+ } Catch {
+ # $Variables.StatusText = "Proxy Request Failed - Trying Direct: $($URi)"
+ }
+ }
+ if (!$Request.Content -or ($Request.StatusCode -ne 200 -and $Request.StatusCode -ne 305) -and -not $OutFile) {
+ Try {
+ $Request = Invoke-WebRequest @Args
+ } Catch {
+ # $Variables.StatusText = "Direct Request Failed: $($URi)"
+ }
+ }
+
+ $Request
+
+}
+
+Function Get-CoinIcon {
+ param(
+ [Parameter(Mandatory = $true)]
+ [String]$Symbol,
+ [Parameter(Mandatory = $false)]
+ [Int]$Size=32
+ )
+ $Symbol = $Symbol.Trim()
+ If ($Symbol -like "auto *" -or $Symbol -in @("")) {Return}
+ # If (!$Variables.CoinIcons) {
+ # $Variables | Add-Member -Force @{CoinIcons = (Invoke-WebRequest "https://api.coingecko.com/api/v3/coins/list" | ConvertFrom-Json) | sort Symbol -Unique | Select Symbol,Id,Name,Image}
+ # }
+ # If (($Variables.CoinIcons | ? {$_.Symbol -eq $Symbol}) -and (($Variables.CoinIcons | ? {$_.Symbol -eq $Symbol}).image -eq $Null)) {
+ # ($Variables.CoinIcons | ? {$_.Symbol -eq $Symbol}).image = (Invoke-WebRequest "https://api.coingecko.com/api/v3/coins/$(($Variables.CoinIcons | ? {$_.Symbol -eq $Symbol}).id)" | ConvertFrom-Json).Image
+ # }
+ # ($Variables.CoinIcons | ? {$_.Symbol -eq $Symbol}).image.thumb
+ If (!$Variables.CoinIcons) {
+ $Variables.CoinIcons = [PSCustomObject]@{}
+ (Invoke-WebRequest "https://api.coingecko.com/api/v3/coins/list" | ConvertFrom-Json) | sort Symbol -Unique | ? {$_.Symbol -in $Variables.Miners.Coin} | ForEach {$Variables.CoinIcons | Add-Member -Force @{$_.Symbol = [PSCustomObject]@{id = $_.id}}}
+ }
+ If (!($Variables.CoinIcons.$Symbol.id)) {
+ (Invoke-WebRequest "https://api.coingecko.com/api/v3/coins/list" | ConvertFrom-Json) | sort Symbol -Unique | ? {$_.Symbol -eq $Symbol} | ForEach {$Variables.CoinIcons | Add-Member -Force @{$_.Symbol = [PSCustomObject]@{id = $_.id}}}
+ }
+ If (($Variables.CoinIcons.$Symbol.id) -and !($Variables.CoinIcons.$Symbol.Image)) {
+ $Variables.CoinIcons.$Symbol | Add-Member -Force @{Image = (Invoke-WebRequest "https://api.coingecko.com/api/v3/coins/$($Variables.CoinIcons.$Symbol.id)" | ConvertFrom-Json).Image.Thumb}
+ $Variables.CoinIcons | Convertto-Json | out-file ".\Logs\CoinIcons.json"
+ }
+ $Variables.CoinIcons.$Symbol.Image
+}
+
+Function Load-CoinsIconsCache {
+
+ If (Test-Path ".\Logs\CoinIcons.json") {
+ $Variables.CoinsIconCachePopulating = $True
+ $Variables.CoinIcons = (Get-Content ".\Logs\CoinIcons.json" | ConvertFrom-Json)
+ $Variables.CoinsIconCacheLoaded = $True
+ $Variables.CoinsIconCachePopulating = $False
+ } Else {
+
+ # Setup runspace to load Coins icons cache in background
+ $IconCacheRunspace = [runspacefactory]::CreateRunspace()
+ $IconCacheRunspace.Open()
+ $IconCacheRunspace.SessionStateProxy.SetVariable("Config", $Config)
+ $IconCacheRunspace.SessionStateProxy.SetVariable("Variables", $Variables)
+ $IconCacheRunspace.SessionStateProxy.Path.SetLocation($pwd) | Out-Null
+ $IconCacheLoader = [PowerShell]::Create().AddScript({
+ . .\Includes\include.ps1
+ $Variables.CoinsIconCachePopulating = $True
+
+ If (!$Variables.CoinsIconCacheLoaded) {
+ If (!$Variables.CoinIcons) {
+ # $Variables | Add-Member -Force @{CoinIcons = [PSCustomObject]@{}}
+ $CoinIcons = [PSCustomObject]@{}
+ (Invoke-ProxiedWebRequest "https://api.coingecko.com/api/v3/coins/list" | ConvertFrom-Json) | sort Symbol -Unique | ? {$_.Symbol -in $Variables.Miners.Coin} | ForEach {$CoinIcons | Add-Member -Force @{$_.Symbol = [PSCustomObject]@{id = $_.id}}}
+ }
+ $CoinIcons.PSobject.Properties.Name | sort | foreach {
+ $CoinIcons.$_ | Add-Member -Force @{Image = (Invoke-ProxiedWebRequest "https://api.coingecko.com/api/v3/coins/$($CoinIcons.$_.id)" | ConvertFrom-Json).Image.Thumb}
+ }
+ }
+ $Variables.CoinIcons = CoinIcons
+ $Variables.CoinIcons | Convertto-Json | out-file ".\Logs\CoinIcons.json"
+ $Variables.CoinsIconCacheLoaded = $True
+ $Variables.CoinsIconCachePopulating = $False
+ $Variables.IconCacheRunspaceHandle.Runspace.Close()
+ $Variables.IconCacheRunspaceHandle.Dispose()
+
+ })
+
+ $IconCacheLoader.Runspace = $IconCacheRunspace
+ $Variables.IconCacheRunspaceHandle = $IconCacheLoader.BeginInvoke()
+ }
+}
+
+Function Get-PoolIcon {
+ param(
+ [Parameter(Mandatory = $true)]
+ [String]$Pool,
+ [Parameter(Mandatory = $false)]
+ [Int]$Size=32
+ )
+
+ If (!$Variables.poolapiref -and (Test-Path ".\Config\poolapiref.json")){
+ $Variables.poolapiref = Get-Content ".\Config\poolapiref.json" | ConvertFrom-Json
+ }
+ ($Variables.poolapiref | ? {$_.Name -eq $Pool}).IconURi
+
+}
+
+Function Get-DisplayCurrency {
+ param(
+ [Parameter(Mandatory = $false)]
+ [Decimal]$Value,
+ [Parameter(Mandatory = $false)]
+ [Decimal]$Factor=1
+ )
+
+ $Result = [PSCustomObject]@{
+ Currency = If($Config.Passwordcurrency -eq "BTC") {"$([char]0x20BF)"} Else {$Config.Passwordcurrency}
+ Value = $Value * $Factor
+ RoundedValue = [Math]::Round($This.Value, 3)
+ Unit = ""
+ UnitString = "$($This.Unit)$($This.Currency)"
+ UnitStringPerDay = "$($This.Unit)$($This.Currency)/Day"
+ DisplayString = "$($This.RoundedValue) $($This.Unit)$($This.Currency)"
+ DisplayStringPerDay = "$($This.RoundedValue) $($This.Unit)$($This.Currency)/Day"
+ }
+ $Result | Add-Member -Force -MemberType ScriptProperty -Name 'RoundedValue' -Value{ [Math]::Round($This.Value, 3) }
+ $Result | Add-Member -Force -MemberType ScriptProperty -Name 'UnitString' -Value{ "$($This.Unit)$($This.Currency)" }
+ $Result | Add-Member -Force -MemberType ScriptProperty -Name 'UnitStringPerDay' -Value{ "$($This.Unit)$($This.Currency)/Day" }
+ $Result | Add-Member -Force -MemberType ScriptProperty -Name 'DisplayString' -Value{ "$($This.RoundedValue) $($This.Unit)$($This.Currency)" }
+ $Result | Add-Member -Force -MemberType ScriptProperty -Name 'DisplayStringPerDay' -Value{ "$($This.RoundedValue) $($This.Unit)$($This.Currency)/Day" }
+
+ $Result.Unit = Switch ([Math]::Floor($Result.Value)) {
+ {$_ -le 0} {"m";$Result.Value*=1000;Break}
+ {$_ -le 999} {"";Break}
+ {$_ -le 999999} {"K";$Result.Value/=1000;Break}
+ {$_ -le 999999999} {"M";$Result.Value/=1000000;Break}
+ {$_ -le 999999999999} {"G";$Result.Value/=1000000000;Break}
+ }
+ # $Result.Value = [Math]::Round($Result.Value, 3)
+ $Result
+}
+
+Function ConvertTo-ImagePath {
+ param(
+ [Parameter(Mandatory = $true)]
+ [String]$Text
+ )
+
+ $ImagePath = Switch ($Text) {
+ "CPU" {"https://img.icons8.com/metro/26/000000/electronics.png"}
+ "NVIDIA" {"https://www.nvidia.com/favicon.ico"}
+ "AMD" {"https://www.amd.com/themes/custom/amd/favicon.ico"}
+ "Top" {"https://img.icons8.com/metro/32/000000/double-up.png"}
+ "Paused" {"https://img.icons8.com/flat_round/64/000000/pause--v1.png"}
+ }
+ $ImagePath
+}
\ No newline at end of file
diff --git a/Includes/MinerCustomConfig.ps1 b/Includes/MinerCustomConfig.ps1
index b74fe37..040689a 100644
--- a/Includes/MinerCustomConfig.ps1
+++ b/Includes/MinerCustomConfig.ps1
@@ -23,10 +23,13 @@ version: 5.9.9
version date: 20191108
#>
- $MinerCustomConfig = $MinerCustomConfig | ? {$_.Enabled}
+ $AbortCurrentPool = $False
+ $DontUseCustom = $False
+ $MinerCustomConfig = $MinerCustomConfig.Where({$_.Enabled})
$Combinations = $MinerCustomConfig | group algo,Pool,miner,coin
$CustomCommands = [PSCustomObject]@{}
$DontUseCustom = $False
+ $DevCommand = ""
$WinningCustomConfig = $null
$CurrentCombination = $null
If ($Pool.Algorithm) {$CustomCommands | Add-Member -Force @{($Pool.Algorithm) = $Commands.($Algo)}}
@@ -40,7 +43,9 @@ version date: 20191108
#Apply user Args
# Test custom config for Algo, coin, Miner, Coin
#PrioritizedCombinations | highest at bottom
- @(
+
+ $OrderedCombinations = @(
+ ", , $($Name), ",
"$($Pool.Algorithm), , , ",
"$($Pool.Algorithm), $($Pool.Name), , ",
"$($Pool.Algorithm), , $($Name), ",
@@ -48,32 +53,28 @@ version date: 20191108
"$($Pool.Algorithm), $($Pool.Name), , $($Pool.Coin)"
"$($Pool.Algorithm), , , $($Pool.Coin)"
"$($Pool.Algorithm), $($Pool.Name), $($Name), $($Pool.Coin)"
- ) | foreach {
- if ($_ -in $Combinations.name) {
- $CurrentCombination = $_
- $WinningCustomConfig = ($Combinations | ? {$_.name -eq $CurrentCombination}).group[0]
-
- If ($WinningCustomConfig.code) {
- $WinningCustomConfig.code | Invoke-Expression
- # Can't get return or continue to work in context correctly when inserted in custom code.
- # Workaround with variable. So users have a way to not apply custom config based on conditions.
- If ($DontUseCustom) {Return}
- }
- If ($WinningCustomConfig.CustomPasswordAdds) {
- $CustomPasswordAdds = $WinningCustomConfig.CustomPasswordAdds.Trim()
- $Password = Merge-Command -Slave $Password -Master $CustomPasswordAdds -Type "Password"
- }
- If ($WinningCustomConfig.CustomCommandAdds) {
- $CustomCmdAdds = $WinningCustomConfig.CustomCommandAdds.Trim()
- $CustomCmdAdds = Merge-Command -Slave $DevCommand -Master $CustomCmdAdds -Type "Command"
- }
+ )
+ $WinningCustomConfig = ($Combinations.Where({$_.Name -like (Compare-Object $OrderedCombinations $Combinations.Name -IncludeEqual -ExcludeDifferent -PassThru | select -Last 1)})).Group
+ if ($WinningCustomConfig) {
+ If ($WinningCustomConfig.IncludeCoins -and $Pool.Coin -notin $WinningCustomConfig.IncludeCoins) {$AbortCurrentPool = $true ; Return}
+ If ($WinningCustomConfig.ExcludeCoins -and $Pool.Coin -in $WinningCustomConfig.ExcludeCoins) {$AbortCurrentPool = $true ; Return}
+ If ($WinningCustomConfig.code) {
+ $WinningCustomConfig.code | Invoke-Expression
+ # Can't get return or continue to work in context correctly when inserted in custom code.
+ # Workaround with variable. So users have a way to not apply custom config based on conditions.
+ If ($DontUseCustom) {Return}
+ }
+ If ($WinningCustomConfig.CustomPasswordAdds -and !($Variables.DonationStart -or $Variables.DonationRunning)) {
+ $CustomPasswordAdds = $WinningCustomConfig.CustomPasswordAdds.Trim()
+ $Password = Merge-Command -Slave $Password -Master $CustomPasswordAdds -Type "Password"
+ }
+ If ($WinningCustomConfig.CustomCommandAdds) {
+ $CustomCmdAdds = $WinningCustomConfig.CustomCommandAdds.Trim()
+ $CustomCmdAdds = Merge-Command -Slave $DevCommand -Master $CustomCmdAdds -Type "Command"
}
- $CustomPasswordAdds = $null
- $CustomCommandAdds = $null
}
+ $CustomPasswordAdds = $null
- If ($WinningCustomConfig.IncludeCoins -and $Pool.Coin -notin $WinningCustomConfig.IncludeCoins) {$AbortCurrentPool = $true ; return}
- If ($WinningCustomConfig.ExcludeCoins -and $Pool.Coin -in $WinningCustomConfig.ExcludeCoins) {$AbortCurrentPool = $true ; return}
$WinningCustomConfig = $null
diff --git a/Includes/MinerCustomConfigEditor.ps1 b/Includes/MinerCustomConfigEditor.ps1
index c9333bb..2c03e3d 100644
--- a/Includes/MinerCustomConfigEditor.ps1
+++ b/Includes/MinerCustomConfigEditor.ps1
@@ -103,7 +103,7 @@ $HelpLabel_OpenLink=
}
#----------------------------------------------
#region Generated Form Code
-$form1.Text = "NPlusMiner Custom Miners Confiuration Editor"
+$form1.Text = "NPlusMiner Custom Miners Configuration Editor"
$NPMIcon = New-Object system.drawing.icon (".\Includes\NPM.ICO")
$form1.Icon = $NPMIcon
$form1.Name = "form1"
@@ -476,4 +476,4 @@ $form1.ShowDialog()| Out-Null
} #End Function
#Call the Function
-GenerateForm
\ No newline at end of file
+GenerateForm
diff --git a/Includes/Server.ps1 b/Includes/Server.ps1
new file mode 100644
index 0000000..61e891a
--- /dev/null
+++ b/Includes/Server.ps1
@@ -0,0 +1,872 @@
+<#
+This file is part of NPlusMiner
+Copyright (c) 2018 Nemo
+Copyright (c) 2018-2020 MrPlus
+
+NPlusMiner is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+NPlusMiner is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+#>
+
+<#
+Product: NPlusMiner
+File: Server.ps1
+version: 6.2.0
+version date: 20201208
+#>
+
+
+function Test-ServerRules {
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory = $true)]
+ [Int]$Port,
+ [Parameter(Mandatory = $false)]
+ [string]$Type = "all" # all, urlacl, firewall, firewall-tcp, firewall-udp
+ )
+ $ServerRulesStatus = $true
+ if ($ServerRulesStatus -and ($Type -eq "firewall" -or $Type -eq "firewall-tcp" -or $Type -eq "all")) {
+ $RuleName = "NPlusMiner Server $($Port) TCP"
+ $RuleACLs = & netsh advfirewall firewall show rule name="$($RuleName)" | Out-String
+ if (-not $RuleACLs.Contains($RuleName)) {$ServerRulesStatus = $false}
+ }
+ if ($ServerRulesStatus -and ($Type -eq "firewall" -or $Type -eq "firewall-udp" -or $Type -eq "all")) {
+ $RuleName = "NPlusMiner Server $($Port) UDP"
+ $RuleACLs = & netsh advfirewall firewall show rule name="$($RuleName)" | Out-String
+ if (-not $RuleACLs.Contains($RuleName)) {$ServerRulesStatus = $false}
+ }
+ if ($ServerRulesStatus -and ($Type -eq "urlacl" -or $Type -eq "all")) {
+ $urlACLs = & netsh http show urlacl | Out-String
+ if (-not $urlACLs.Contains("http://+:$($Port)/")) {$ServerRulesStatus = $false}
+ }
+ $ServerRulesStatus
+}
+
+function Initialize-ServerRules {
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory = $true)]
+ [Int]$Port
+ )
+
+ if (-not (Test-ServerRules -Port $Port -Type "urlacl")) {
+ # S-1-5-32-545 = SID for Users group
+ (Start-Process netsh -Verb runas -PassThru -ArgumentList "http add urlacl url=http://+:$($Port)/ sddl=D:(A;;GX;;;S-1-5-32-545) user=everyone").WaitForExit(5000)>$null
+ }
+
+ if (-not (Test-ServerRules -Port $Port -Type "firewall-tcp")) {
+ (Start-Process netsh -Verb runas -PassThru -ArgumentList "advfirewall firewall add rule name=`"NPlusMiner Server $($Port) TCP`" dir=in action=allow protocol=TCP localport=$($Port)").WaitForExit(5000)>$null
+ }
+
+ if (-not (Test-ServerRules -Port $Port -Type "firewall-udp")) {
+ (Start-Process netsh -Verb runas -PassThru -ArgumentList "advfirewall firewall add rule name=`"NPlusMiner Server $($Port) UDP`" dir=in action=allow protocol=UDP localport=$($Port)").WaitForExit(5000)>$null
+ }
+}
+
+function Reset-ServerRules {
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory = $true)]
+ [Int]$Port
+ )
+
+ if (Test-ServerRules -Port $Port -Type "urlacl") {
+ (Start-Process netsh -Verb runas -PassThru -ArgumentList "http delete urlacl url=http://+:$($Port)/").WaitForExit(5000)>$null
+ }
+
+ if (Test-ServerRules -Port $Port -Type "firewall") {
+ (Start-Process netsh -Verb runas -PassThru -ArgumentList "advfirewall firewall delete rule name=`"NPlusMiner Server $($Port) TCP`"").WaitForExit(5000)>$null
+ (Start-Process netsh -Verb runas -PassThru -ArgumentList "advfirewall firewall delete rule name=`"NPlusMiner Server $($Port) UDP`"").WaitForExit(5000)>$null
+ }
+}
+
+Function Start-Server {
+ if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+ Initialize-ServerRules $Config.Server_Port
+
+ # Setup runspace to launch the API webserver in a separate thread
+ $ServerRunspace = [runspacefactory]::CreateRunspace()
+ $ServerRunspace.Open()
+ $ServerRunspace.SessionStateProxy.SetVariable("Config", $Config)
+ $ServerRunspace.SessionStateProxy.SetVariable("Variables", $Variables)
+ $ServerRunspace.SessionStateProxy.Path.SetLocation($pwd) | Out-Null
+
+ $Server = [PowerShell]::Create().AddScript({
+ . .\Includes\include.ps1
+
+ Load-CoinsIconsCache
+
+ Function Get-StringHash([String] $String,$HashName = "MD5")
+ {
+ $StringBuilder = New-Object System.Text.StringBuilder
+ [System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))|%{
+ [Void]$StringBuilder.Append($_.ToString("x2"))
+ }
+ $StringBuilder.ToString()
+ }
+ [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
+
+ $BasePath = "$($pwd)"
+ if ($MyInvocation.MyCommand.Path) {
+ Set-Location (Split-Path $MyInvocation.MyCommand.Path)
+ $BasePath = $MyInvocation.MyCommand.Path
+ }
+
+ $MIMETypes = @{
+ ".js" = "application/x-javascript"
+ ".html" = "text/html"
+ ".htm" = "text/html"
+ ".json" = "application/json;charset=UTF-8"
+ ".css" = "text/css"
+ ".txt" = "text/plain"
+ ".ico" = "image/x-icon"
+ ".ps1" = "text/html" # ps1 files get executed, assume their response is html
+ ".png" = "image/png"
+ }
+
+ # Load Branding
+ If (Test-Path ".\Config\Branding.json") {
+ $Branding = Get-Content ".\Config\Branding.json" | ConvertFrom-Json
+ } Else {
+ $Branding = [PSCustomObject]@{
+ LogoPath = "http://tiny.cc/yvlaoz"
+ BrandName = "NPlusMiner"
+ BrandWebSite = "https://github.com/MrPlusGH/NPlusMiner"
+ ProductLable = "NPlusMiner"
+ }
+ }
+
+ If (Test-Path ".\Logs\Server.log") {Remove-Item ".\Logs\Server.log" -Force}
+ Start-Transcript ".\Logs\ServerTR.log"
+ # $pid | out-host
+ if ([Net.ServicePointManager]::SecurityProtocol -notmatch [Net.SecurityProtocolType]::Tls12) {
+ [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12
+ }
+
+ [System.Collections.ArrayList]$ProxyCache = @()
+
+ [System.Collections.ArrayList]$Clients = @()
+
+ $ServerListener = New-Object Net.HttpListener
+ $ServerListener.Prefixes.Add("http://+:$($Config.Server_Port)/")
+ $ServerListener.AuthenticationSchemes = [System.Net.AuthenticationSchemes]::Basic
+
+
+
+ #ignore self-signed/invalid ssl certs
+ # Breaks TLS all up !
+ # [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$True}
+
+ Foreach ($P in $Up) {$Hso.Prefixes.Add($P)}
+ $ServerListener.Start()
+ While ($ServerListener.IsListening -and -not $Variables.StopServer) {
+ if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+ # $HC = $ServerListener.GetContext()
+
+ $contextTask = $ServerListener.GetContextAsync()
+ while (-not $contextTask.AsyncWaitHandle.WaitOne(500)) { }
+ $HC = $contextTask.GetAwaiter().GetResult()
+
+ $HReq = $HC.Request
+ # $Hreq | Out-Host
+ # $Hreq | convertto-json -Depth 10 | Out-File ".\Logs\HReq.json"
+ $Path = $Hreq.Url.LocalPath
+ $ClientAddress = $Hreq.RemoteEndPoint.Address.ToString()
+ $ClientPort = $Hreq.RemoteEndPoint.Port
+ $HRes = $HC.Response
+ # $HRes.Headers.Add("Content-Type","text/html")
+
+ If (($Clients.Where({$_.Address -eq $ClientAddress})).count -lt 1) {
+ $Clients.Add([PSCustomObject]@{
+ Address = $ClientAddress
+ })
+ }
+ # $Hreq.RemoteEndPoint | Out-host
+ # $ProxURL | Out-Host
+
+ # If ("Proxy-Connection" -in $HReq.Headers -and $ProxURL) {
+ # If ($ProxURL) {
+ if((-not $HC.User.Identity.IsAuthenticated -or $HC.User.Identity.Name -ne $Config.Server_User -or $HC.User.Identity.Password -ne $Config.Server_Password)) {
+ $Data = "Access denied"
+ $StatusCode = [System.Net.HttpStatusCode]::Forbidden
+ $ContentType = "text/html"
+ $AuthSuccess = $False
+ } else {
+
+# Define Page Header
+ $Header =
+@"
+
+
+
+
+ Copyright (c) 2018-$((Get-Date).year) MrPlus
+
+
+ $(Get-Date)     $($Branding.ProductLable) $($Variables.CurrentVersion)     Runtime $(("{0:dd\ \d\a\y\s\ hh\:mm}" -f ((get-date)-$Variables.ScriptStartDate)))     Path: $($BasePath)     API Cache hit ratio: $("{0:N0}" -f $CacheHitsRatio)%
+ Worker Name: $($Config.WorkerName)
+     Average Profit: $(((Get-DisplayCurrency ($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum)).DisplayStringPerDay)
+     $(If($Variables.Rates.($Config.Currency) -gt 0){"$($Config.Passwordcurrency)/$($Config.Currency) $($Variables.Rates.($Config.Currency).ToString("N2"))"})
+
+
+ Running Miners      Benchmarks      Switching log
+"@
+
+ If ($Variables.Paused) {
+ $Header += "       Start Mining "
+ } Else {
+ $Header += "       Pause Mining "
+ }
+ $Header +=
+@"
+
+
+
+"@
+
+ #
+ #
+
+ If (Test-Path ".\Config\Peers.json") {
+ $Header += "Rigs:     "
+ (get-content ".\Config\Peers.json" | ConvertFrom-Json) | Sort Name | foreach {
+ $Peer = $_
+ $Header += "$($Peer.Name)      "
+ }
+ $Header += " "
+ }
+
+# Define Page Footer
+ $Footer =
+@"
+
+
+ Copyright (c) 2018-2020 MrPlus
+
+ icons8.com
+
+
+"@
+
+
+ $AuthSuccess = $True
+ $HReq.RawUrl | write-Host
+ Switch($Path) {
+ "/StopServer" {
+ write-host "Stop Requested"
+ $Variables.StopServer = $True
+ $Variables.ServerRunning = $False
+ $Hso.Close()
+ $ContentType = "text/html"
+ $Content = "OK"
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/proxy/" {
+ $ProxyCache = $ProxyCache.Where({$_.Date -ge (Get-Date).AddMinutes(-$Config.Server_ServerProxyTimeOut)})
+ $ProxURL = $HReq.RawUrl.Replace("/Proxy/?url=","")
+ # $ProxURL = $HReq.QueryString['URL']
+ $ProxURLHash = Get-StringHash $ProxURL
+
+ If (($ProxyCache.Where({$_.ID -eq $ProxURLHash -and $_.date -ge (Get-Date).AddMinutes(-$Config.Server_ServerProxyTimeOut)})).Content -ne $null) {
+ # "Get cache content" | Out-Host
+ $CacheHits++
+ $Content = ($ProxyCache.Where({$_.ID -eq $ProxURLHash})).Content
+ $StatusCode = [System.Net.HttpStatusCode]::UseProxy
+ } else {
+ # "Web Query" | Out-Host
+ $WebHits++
+ $Wco = New-Object Net.Webclient
+ $Content = $Wco.downloadString("$ProxURL")
+ If ($Content) {
+ $ProxyCache = $ProxyCache.Where({$_.ID -ne $ProxURLHash})
+ $ProxyCache.Add([PSCustomObject]@{
+ ID = $ProxURLHash
+ URL = $ProxURL
+ Date = Get-Date
+ Content = $Content
+ })
+ }
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ $Wco.Dispose()
+ }
+
+ If (($CacheHits + $WebHits)) {$CacheHitsRatio = $CacheHits / ($CacheHits + $WebHits) * 100}
+ Break
+ }
+ "/RegisterRig/" {
+ $ContentType = "text/html"
+
+ $Peers = @()
+ $PeerUpdate = $False
+
+ $RegisterRigName = $HReq.QueryString['Name']
+ $RegisterRigIP = $HReq.QueryString['IP']
+ $RegisterRigPort = $HReq.QueryString['Port']
+ $RegisterBackRegistrationNotAllowed = If ($HReq.QueryString['BackRegistrationNotAllowed'] -eq "true") {$True} Else {$False}
+
+ If (!$RegisterRigName -or (!$RegisterRigIP -and !$ClientAddress) -or !$RegisterRigPort) {
+ $StatusCode = 404
+ $Content = "Incomplete registration"
+ } Else {
+ $Peer = [PSCustomObject]@{
+ Name = $RegisterRigName
+ IP = If (!$RegisterRigIP) {$ClientAddress} Else {$RegisterRigIP}
+ Port = $RegisterRigPort
+ }
+ If (Test-Path ".\Config\Peers.json") {
+ $Peers = Get-Content ".\Config\Peers.json" | convertfrom-json
+ If (@($Peers).count -eq 1) { $Peers = @($Peers) }
+ }
+
+ If (($Peers | ? {$_.Name -eq $RegisterRigName}) -and !(compare $Peer $Peers -IncludeEqual -ExcludeDifferent) -and !($Peers | ? {$_.Name -eq $RegisterRigName}).PreventUpdates) {
+ ($Peers | ? {$_.Name -eq $RegisterRigName}).IP = $Peer.IP
+ ($Peers | ? {$_.Name -eq $RegisterRigName}).Port = $Peer.Port
+ $PeerUpdate = $True
+ } elseif (!($Peers | ? {$_.Name -eq $RegisterRigName})) {
+ $Peers += $Peer
+ $PeerUpdate = $True
+ }
+
+ $PeerPing =
+ If ( $Peer.Name -eq $Config.WorkerName ) {
+ $True
+ } Else {
+ Try { (Invoke-WebRequest "http://$($Peer.IP):$($Peer.Port)/ping" -Credential $Variables.ServerClientCreds -TimeoutSec 3).content -eq "Server Alive" } Catch {$False}
+ }
+
+ If ($PeerUpdate -and $PeerPing) {
+ $Peers | convertto-json | out-file ".\Config\Peers.json"
+ If ($RegisterRigName -ne $Config.WorkerName -and !$RegisterBackRegistrationNotAllowed){
+ # Back registration won't work until we switch the listener to ASync
+ # Try { (Invoke-WebRequest "http://$($Peer.IP):$($Peer.Port)/RegisterRig/?Name=$($Config.WorkerName)&Port=$($Config.Server_Port)&RegisterBackRegistrationNotAllowed=true" -Credential $Variables.ServerClientCreds -TimeoutSec 3).content -eq "Server Alive" } Catch {$False}
+ }
+
+ $Content = "$($Peer.Name)`n$($Peer.IP)`n$($Peer.Port)"
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ } Else {
+ $StatusCode = 404
+ $Content = "Peer not responding"
+ }
+ }
+ $Peers = $Null
+ Break
+ }
+ "/ping" {
+ $ContentType = "text/html"
+ $Content = "Server Alive"
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/ClearCache" {
+ $ContentType = "text/html"
+ $CacheHits = 0
+ $WebHits = 0
+ rv ProxyCache
+ [System.Collections.ArrayList]$ProxyCache = @()
+ $Content = "OK"
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/ExportCache" {
+ $ContentType = "text/html"
+ $ProxyCache | convertto-json | Out-File ".\logs\ProxyCache.json"
+ $Content = "OK"
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Config.json" {
+ $Title = "Config.json"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ $ContentType = $MIMETypes[".json"]
+ $Content = $Config | ConvertTo-Json -Depth 10
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Variables.json" {
+ $Title = "Variables.json"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ $ContentType = $MIMETypes[".json"]
+ $Content = $Variables | ConvertTo-Json -Depth 10
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Earnings.json" {
+ $Title = "Earnings.json"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ $ContentType = $MIMETypes[".json"]
+ $Content = $Variables.Earnings | ConvertTo-Json -Depth 10
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/RunningMiners.json" {
+ $Title = "RunningMiners.json"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ $ContentType = $MIMETypes[".json"]
+ $Content = $Variables.ActiveMinerPrograms.Clone().Where( {$_.Status -eq "Running"} ) | Sort Type | ConvertTo-Json -Depth 10
+ If ($Variables.Paused) {
+ $Content = [PSCustomObject]@{
+ Type = "Paused"
+ } | ConvertTo-Json
+ }
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Benchmarks.json" {
+ $Title = "Benchmarks.json"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ $ContentType = $MIMETypes[".json"]
+ $Content = $Variables.Miners | ConvertTo-Json -Depth 10
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Peers.json" {
+ $Title = "Peers.json"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ $ContentType = $MIMETypes[".json"]
+ If (Test-Path ".\Config\Peers.json") {$Content = Get-Content ".\Config\Peers.json" -Raw}
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Status" {
+ $Title = "Status"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ $ContentType = $MIMETypes[".html"]
+
+ $EarningsTrends = [PSCustomObject]@{}
+ $TrendSign = switch ([Math]::Round((($Variables.Earnings.Values | measure -Property Growth1 -Sum).sum*1000*24),3) - [Math]::Round((($Variables.Earnings.Values | measure -Property Growth6 -Sum).sum*1000*4),3)) {
+ {$_ -eq 0}
+ {"   "}
+ {$_ -gt 0}
+ {"   "}
+ {$_ -lt 0}
+ {"   "}
+ }
+ $EarningsTrends | Add-Member -Force @{"Last 1h $TrendSign" = ((Get-DisplayCurrency ($Variables.Earnings.Values | measure -Property Growth1 -Sum).sum 24)).DisplayStringPerDay}
+ $TrendSign = switch ([Math]::Round((($Variables.Earnings.Values | measure -Property Growth6 -Sum).sum*1000*4),3) - [Math]::Round((($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum*1000),3)) {
+ {$_ -eq 0}
+ {"   "}
+ {$_ -gt 0}
+ {"   "}
+ {$_ -lt 0}
+ {"   "}
+ }
+ $EarningsTrends | Add-Member -Force @{"Last 6h $TrendSign" = ((Get-DisplayCurrency ($Variables.Earnings.Values | measure -Property Growth6 -Sum).sum 4)).DisplayStringPerDay}
+ $TrendSign = switch ([Math]::Round((($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum*1000),3) - [Math]::Round((($Variables.Earnings.Values | measure -Property BTCD -Sum).sum*1000*0.96),3)) {
+ {$_ -eq 0}
+ {"   "}
+ {$_ -gt 0}
+ {"   "}
+ {$_ -lt 0}
+ {"   "}
+ }
+ $EarningsTrends | Add-Member -Force @{"Last 24h $TrendSign" = ((Get-DisplayCurrency ($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum)).DisplayStringPerDay}
+ $Header += $EarningsTrends | ConvertTo-Html -CssUri "./Includes/Web.css"
+
+ If (Test-Path ".\logs\DailyEarnings.csv"){
+ $Chart1 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'Front7DaysEarnings' -Width 505 -Height 85 -Currency $($Config.Passwordcurrency)"
+ $Chart2 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'DayPoolSplit' -Width 200 -Height 85 -Currency $($Config.Passwordcurrency)"
+
+
+ $Header +=
+@"
+
+
+ Earnings Tracker
+
+
+
+
+
+
+ Past 7 days earnings
+ Per pool earnings
+
+
+
+
+
+
+
+
+
+
+
+
+"@
+ }
+
+ If ($Variables.Earnings -and $Config.TrackEarnings) {
+ $DisplayEarnings = [System.Collections.ArrayList]@($Variables.Earnings.Values | select @(
+ @{Name="Pool";Expression={"   " +$_.Pool}},
+ @{Name="Trust";Expression={"{0:P0}" -f $_.TrustLevel}},
+ @{Name="Balance";Expression={$_.Balance}},
+ # @{Name="Unpaid";Expression={$_.total_unpaid}},
+ # @{Name="BTC/D";Expression={"{0:N8}" -f ($_.BTCD)}},
+ @{Name="1h $((Get-DisplayCurrency $_.Growth1 24).UnitStringPerDay)";Expression={(Get-DisplayCurrency $_.Growth1 24).RoundedValue}},
+ @{Name="6h $((Get-DisplayCurrency $_.Growth6 4).UnitStringPerDay)";Expression={(Get-DisplayCurrency $_.Growth6 4).RoundedValue}},
+ @{Name="24h $((Get-DisplayCurrency $_.Growth24).UnitStringPerDay)";Expression={(Get-DisplayCurrency $_.Growth24).RoundedValue}},
+
+ @{Name = "Est. Pay Date"; Expression = {if ($_.EstimatedPayDate -is 'DateTime') {$_.EstimatedPayDate.ToShortDateString()} else {$_.EstimatedPayDate}}},
+
+ @{Name="PaymentThreshold";Expression={"$($_.PaymentThreshold) ($('{0:P0}' -f $($_.Balance / $_.PaymentThreshold)))"}}#,
+ # @{Name="Wallet";Expression={$_.Wallet}}
+ ) | Sort "1h $((Get-DisplayCurrency $_.Growth1 24).UnitStringPerDay)","6h $((Get-DisplayCurrency $_.Growth6 4).UnitStringPerDay)","24h $((Get-DisplayCurrency $_.Growth24).UnitStringPerDay)" -Descending)
+ $DisplayEarnings = [System.Collections.ArrayList]@($DisplayEarnings) | ConvertTo-Html -CssUri "./Includes/Web.css" -Title $Title -PreContent $Header
+ $Content = [System.Web.HttpUtility]::HtmlDecode($DisplayEarnings)
+ }
+ $Content +=
+@"
+
+
+ Running Miners
+
+
+"@
+
+ If (Test-Path ".\Config\Peers.json") {
+ $Peers = Get-Content ".\Config\Peers.json" | ConvertFrom-Json
+ } Else {
+ $Peers = @([PSCustomObject]@{ Name = $Config.WorkerName ; IP = "127.0.0.1" ; Port = $Config.Server_Port })
+ }
+ $Miners = @()
+ $Peers | foreach {
+ $Peer = $_
+ If ($Peer.Name -eq $Config.WorkerName) {
+ $Miners += $Variables.ActiveMinerPrograms.Clone().Where( {$_.Status -eq "Running"} ) | select @{Name = "Rig";Expression={$Peer.Name}},*
+ } else {
+ $Miners += (Invoke-WebRequest "http://$($Peer.IP):$($Peer.Port)/RunningMiners.json" -Credential $Variables.ServerCreds -TimeoutSec 5 | ConvertFrom-Json) | select @{Name = "Rig";Expression={$Peer.Name}},*
+ }
+ }
+ $MinersTable = [System.Collections.ArrayList]@($Miners | select @(
+ @{Name = "Rig";Expression={$_.Rig}},
+ @{Name = "Type";Expression={$_.Type}},
+ @{Name = "Algorithm";Expression={$_.Algorithms}},
+ # @{Name = "Coin"; Expression={"###CoinIcon###$($_.Coin.ToLower())###IconSize###" + $_.Coin}},
+ # @{Name = "Coin"; Expression={If($_.Coin -and $_.Coin -ne ""){"   " + $_.Coin}else{""}}},
+ @{Name = "Coin"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {
+ Try {
+ "   " + $_.Coin
+ } Catch {""}
+ }}},
+ @{Name = "Miner";Expression={$_.Name}},
+ @{Name = "HashRate";Expression={"$($_.HashRate | ConvertTo-Hash)/s"}},
+ @{Name = "Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f [TimeSpan]$_.Active.Ticks}},
+ @{Name = "Total Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f [TimeSpan]$_.TotalActive.Ticks}},
+ # @{Name = "Pool"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"$($_.Name)"}}} ) | sort Rig,Type
+ @{Name = "Pool"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"   " + $_.Name}}} ) | sort Rig,Type
+ ) | ConvertTo-Html -CssUri "./Includes/Web.css"
+ # $MinersTable = $MinersTable -Replace "###CoinIcon###", "   "
+
+ $MinersTable = [System.Web.HttpUtility]::HtmlDecode($MinersTable)
+
+ # $MinersTable = [regex]::Replace($MinersTable,'###CoinIcon###(.*)###IconSize###',{param($match) "   "})
+
+ $Content += $MinersTable
+
+ ForEach ($Type in ($Miners.Type | Sort -Unique)) {
+ $Content = $Content -Replace "$($Type) ", "   $($Type) "
+ }
+
+ $Content += $Footer
+ $Content = [System.Web.HttpUtility]::HtmlDecode($Content)
+
+ # $Content = $Header + $Content
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/RunningMiners" {
+ $Title = "Running Miners"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ $ContentType = $MIMETypes[".html"]
+ $Content = [System.Collections.ArrayList]@($Variables.ActiveMinerPrograms.Clone() | ? {$_.Status -eq "Running"} | select @(
+ @{Name = "Type";Expression={$_.Type}},
+ @{Name = "Algorithm";Expression={$_.Algorithms}},
+ @{Name = "Coin"; Expression={$_.Coin}},
+ @{Name = "Miner";Expression={$_.Name}},
+ @{Name = "HashRate";Expression={"$($_.HashRate | ConvertTo-Hash)/s"}},
+ @{Name = "Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f $_.Active}},
+ @{Name = "Total Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f $_.TotalActive}},
+ # @{Name = "Host";Expression={$_.Host}},
+ @{Name = "Pool"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"$($_.Name)"}}}
+ ) | sort Type
+ ) | ConvertTo-Html -CssUri "./Includes/Web.css" -Title $Title -PreContent $Header
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/PeersRunningMiners" {
+ $Title = "Peers Running Miners"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ $ContentType = $MIMETypes[".html"]
+
+ If (Test-Path ".\Config\Peers.json") {
+ $Peers = Get-Content ".\Config\Peers.json" | ConvertFrom-Json
+ } Else {
+ $Peers = @([PSCustomObject]@{ Name = $Config.WorkerName ; IP = "127.0.0.1" ; Port = $Config.Server_Port })
+ }
+ $Miners = @()
+
+ $Peers | foreach {
+ $Peer = $_
+ If ($Peer.Name -eq $Config.WorkerName) {
+ $Miners += $Variables.ActiveMinerPrograms.Clone() | ? {$_.Status -eq "Running"} | select @{Name = "Rig";Expression={$Peer.Name}},*
+ } else {
+ $Miners += (Invoke-WebRequest "http://$($Peer.IP):$($Peer.Port)/RunningMiners.json" -Credential $Variables.ServerCreds | ConvertFrom-Json) | select @{Name = "Rig";Expression={$Peer.Name}},*
+ }
+ }
+ $MinersTable = [System.Collections.ArrayList]@($Miners | select @(
+ @{Name = "Rig";Expression={$_.Rig}},
+ @{Name = "Type";Expression={$_.Type}},
+ @{Name = "Algorithm";Expression={$_.Algorithms}},
+ @{Name = "Coin"; Expression={"###CoinIcon###$($_.Coin.ToLower())###IconSize###" + $_.Coin}},
+ @{Name = "Miner";Expression={$_.Name}},
+ @{Name = "HashRate";Expression={"$($_.HashRate | ConvertTo-Hash)/s"}},
+ @{Name = "Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f [TimeSpan]$_.Active.Ticks}},
+ @{Name = "Total Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f [TimeSpan]$_.TotalActive.Ticks}},
+ @{Name = "Pool"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"$($_.Name)"}}} ) | sort Rig,Type
+ ) | ConvertTo-Html -CssUri "./Includes/Web.css" -Title $Title -PreContent $Header
+ $MinersTable = $MinersTable -Replace "###CoinIcon###", "   "
+ $Content = $MinersTable
+ # $Content = $Header + $Content
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Benchmarks" {
+ $Title = "Benchmarks"
+ # $Content = ConvertTo-Html -CssUri "file:///d:/Nplusminer/Includes/Web.css " -Title $Title -Body "$Title `nUpdated: on $(Get-Date) "
+ If (!$Variables.CoinsIconCacheLoaded -and !$Variables.CoinsIconCachePopulating) {Load-CoinsIconsCache}
+
+ $ContentType = $MIMETypes[".html"]
+ $Content = $Header
+ $Content += " "
+
+ $Miners = $Variables["Miners"].Clone()
+
+ ForEach ($Type in ($Miners.Type | Sort -Unique -Descending)) {
+ $Content += "   $($Type)     "
+ }
+
+ ForEach ($Type in ($Miners.Type | Sort -Unique -Descending)) {
+ $Content +=
+@"
+
+
+
+   $($Type)
+
+
+"@
+
+ $DisplayEstimations = [System.Collections.ArrayList]@($Miners.Where( {$_.Type -eq $Type} ) | sort $_.Profits.PSObject.Properties.Value -Descending | Select @(
+ @{Name = "Type";Expression={$_.Type}},
+ @{Name = "Miner";Expression={$_.Name}},
+ @{Name = "Algorithm";Expression={$_.HashRates.PSObject.Properties.Name}},
+ # @{Name = "Coin"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"$($_.Coin)"}}},
+ @{Name = "Coin"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {
+ If($Variables.CoinsIconCacheLoaded) {
+ Try {
+ "   " + $_.Coin
+ } Catch {$_.Coin}
+ }else{$_.Coin}
+ }}},
+ # @{Name = "Pool"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"$($_.Name)"}}},
+ @{Name = "Pool"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"   " + $_.Name}}},
+ @{Name = "Speed"; Expression={$_.HashRates.PSObject.Properties.Value | ForEach {if($_ -ne $null){"$($_ | ConvertTo-Hash)/s"}else{"Benchmarking"}}}},
+ # @{Name = "mBTC/Day"; Expression={$_.Profits.PSObject.Properties.Value | ForEach {if($_ -ne $null){($_*1000).ToString("N3")}else{"Benchmarking"}}}},
+ @{Name = "mBTC/Day"; Expression={(($_.Profits.PSObject.Properties.Value | Measure -Sum).Sum *1000).ToString("N3")}},
+ # @{Name = "BTC/Day"; Expression={$_.Profits.PSObject.Properties.Value | ForEach {if($_ -ne $null){$_.ToString("N5")}else{"Benchmarking"}}}},
+ # @{Name = "BTC/Day"; Expression={(($_.Profits.PSObject.Properties.Value | Measure -Sum).Sum).ToString("N3")}},
+ # @{Name = "BTC/GH/Day"; Expression={$_.Pools.PSObject.Properties.Value.Price | ForEach {($_*1000000000).ToString("N15")}}}
+ @{Name = "BTC/GH/Day"; Expression={(($_.Pools.PSObject.Properties.Value.Price | Measure -Sum).Sum *1000000000).ToString("N5")}}
+ # ) | sort "mBTC/Day" -Descending) | ConvertTo-Html -CssUri "http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/Includes/Web.css" -Title $Title -PreContent $Header
+ ) | sort "mBTC/Day" -Descending)
+
+ $DisplayEstimations = If ($Config.ShowOnlyTopCoins){
+ [System.Collections.ArrayList]@($DisplayEstimations | sort "mBTC/Day" -Descending | Group "Type","Algorithm" | % { $_.Group | select -First 1})
+ } else {
+ $DisplayEstimations
+ }
+
+ $Content += $DisplayEstimations | ConvertTo-Html -CssUri "./Includes/Web.css" -Title $Title
+
+ }
+
+ ForEach ($Type in ($Miners.Type | Sort -Unique)) {
+ $Content = $Content -Replace "$($Type) ", "   $($Type) "
+ }
+ $Content += $Footer
+ $Content = [System.Web.HttpUtility]::HtmlDecode($Content)
+
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+
+
+ Break
+ }
+ "/SwitchingLog" {
+ $Title = "SwitchingLog"
+ $ContentType = $MIMETypes[".html"]
+
+ If (Test-Path ".\Logs\switching.log"){$SwitchingArray = [System.Collections.ArrayList]@(@((get-content ".\Logs\switching.log" -First 1) , (get-content ".\logs\switching.log" -last 15)) | ConvertFrom-Csv | Select date,type,algo,coin,host -Last 13)}
+ $Content = $SwitchingArray | ConvertTo-Html -CssUri "./Includes/Web.css" -Title $Title -PreContent $Header
+
+ $Content += $Footer
+
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Cmd-CleanIconCache" {
+ $ContentType = "text/html"
+ $Variables.CoinIcons = @()
+ $Variables.CoinsIconCacheLoaded = $False
+ $Variables.CoinsIconCachePopulating = $False
+
+ $Title = "CleanIconCache"
+ $Content = "OK"
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Cmd-Pause" {
+ $ContentType = "text/html"
+ $Variables.StatusText = "Pause Mining requested via API."
+ $Variables.Paused = $True
+ $Variables.RestartCycle = $True
+
+ $Title = "Pause Command"
+ $Content = "OK"
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Cmd-Mine" {
+ $ContentType = "text/html"
+ $Variables.StatusText = "Start Mining requested via API."
+ $Variables.Paused = $False
+ $Variables.LastDonated = (Get-Date).AddDays(-1).AddHours(1)
+ $Variables.RestartCycle = $True
+
+ $Title = "Mine Command"
+ $Content = "OK"
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ "/Cmd-ResetPeers" {
+ $ContentType = "text/html"
+ If (Test-Path ".\Config\Peers.json") {Remove-Item -Recurse -Force ".\Config\Peers.json"}
+
+ $Title = "Peers Reset"
+ $Content = "OK"
+ $StatusCode = [System.Net.HttpStatusCode]::OK
+ Break
+ }
+ default {
+ # Set index page
+ if ($Path -eq "/") {
+ $Path = "/index.html"
+ }
+
+ # Check if there is a file with the requested path
+ $Filename = $BasePath + $Path
+ if (Test-Path $Filename -PathType Leaf -ErrorAction SilentlyContinue) {
+ # If the file is a powershell script, execute it and return the output. A $Parameters parameter is sent built from the query string
+ # Otherwise, just return the contents of the file
+ $File = Get-ChildItem $Filename
+
+ if ($File.Extension -eq ".ps1") {
+ $Content = & $File.FullName -Parameters $Parameters
+ }
+ else {
+ $Content = Get-Content $Filename -Raw
+
+ # Process server side includes for html files
+ # Includes are in the traditional '' format used by many web servers
+ if ($File.Extension -eq ".html") {
+ $IncludeRegex = [regex]''
+ $IncludeRegex.Matches($Content) | Foreach-Object {
+ $IncludeFile = $BasePath + '/' + $_.Groups[1].Value
+ if (Test-Path $IncludeFile -PathType Leaf) {
+ $IncludeData = Get-Content $IncludeFile -Raw
+ $Content = $Content -replace $_.Value, $IncludeData
+ }
+ }
+ }
+ }
+
+ # Set content type based on file extension
+ if ($MIMETypes.ContainsKey($File.Extension)) {
+ $ContentType = $MIMETypes[$File.Extension]
+ Switch ($File.Extension) {
+ ".png" {$Content = [System.IO.File]::ReadAllBytes($File.FullName)}
+ }
+ }
+ else {
+ # If it's an unrecognized file type, prompt for download
+ $ContentType = "application/octet-stream"
+ }
+ }
+ else {
+ $StatusCode = 404
+ $ContentType = "text/html"
+ $Content = "URI '$Path' is not a valid resource. ($Filename)"
+ }
+ }
+ }
+ $HasContent = $content -ne $null
+
+ If ($Content.GetType() -ne [byte[]]) {
+ [byte[]] $Buf = [System.Text.Encoding]::UTF8.GetBytes($Content)
+ } Else {
+ [byte[]] $Buf = $Content
+ }
+
+ # $Buf = [Text.Encoding]::UTF8.GetBytes($Content)
+ $Hres.Headers.Add("Content-Type", $ContentType)
+ $HRes.ContentLength64 = $Buf.Length
+ $HRes.OutputStream.Write($Buf,0,$Buf.Length)
+ $HRes.OutputStream.Flush()
+ $HRes.OutputStream.Dispose()
+ $HRes.Close()
+ $Content = $null
+ $Buf = $null
+ # $ProxyCache | convertto-json | Out-File ".\logs\ProxyCache.json"
+ }
+ if ($Config.Server_Log) {
+ $LogEntry = [PSCustomObject]@{
+ CacheHitRatio = $CacheHitsRatio
+ StatusCode = $StatusCode.value__
+ Date = Get-date
+ ClientAddress = $ClientAddress
+ ClientPort = $ClientPort
+ Path = $Path
+ URL = $ProxURL
+ Content = $HasContent
+ AuthSuccess = $AuthSuccess
+ pid = $pid
+ }
+ $LogEntry | Export-Csv ".\Logs\Server.log" -NoTypeInformation -Append
+ rv LogEntry
+ $ProxURL = ""
+ }
+ $HCTemp = $null
+ }
+ Write-Host "Server stopping"
+ $ServerListener.Stop()
+ $ServerListener.Close()
+ # $Variables.Server.Runspace.Close()
+ # $Variables.Server.Dispose()
+
+ })
+ $Server.Runspace = $ServerRunspace
+ # $Variables.Server | Add-Member -Force @{ServerListener = $ServerListener}
+ $Variables.ServerRunspaceHandle = $Server.BeginInvoke()
+}
+
diff --git a/Includes/Web.css b/Includes/Web.css
new file mode 100644
index 0000000..f3cc3ad
--- /dev/null
+++ b/Includes/Web.css
@@ -0,0 +1,93 @@
+h1, h5, th {
+ text-align: center;
+}
+
+body {
+ background-color: #F0F0F0;
+}
+
+table {
+ margin: auto;
+ /* font-family: Segoe UI; */
+ font-family: 'MS Sans Serif', Geneva, sans-serif;
+ box-shadow: -5px 5px 5px #888;
+ /* border: thin ridge grey; */
+ border: none;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+th {
+ background: #000000;
+ color: #fff;
+ max-width: 400px;
+ padding: 5px 10px;
+ font-weight: normal;
+}
+
+td {
+ font-size: 11px;
+ padding: 5px 20px;
+ color: #000;
+}
+
+tr {
+ background: #b8d1f3;
+}
+
+tr:nth-child(even) {
+ background: #F0F0F0;
+}
+
+tr:nth-child(odd) {
+ /* background: #f2a900; */
+ /* background: #f8d175; */
+ background: #F9F9F9;
+}
+
+header {
+ overflow: auto;
+ background-color: #F9F9F9;
+ /* background: left / no-repeat url(https://raw.githubusercontent.com/MrPlusGH/NPlusMiner/master/Includes/NPM.png); */
+ padding: 5px 20px;
+ color: #000;
+ text-align: center;
+ font-weight: bold;
+}
+
+header img {
+ float: left;
+ padding: 8px;
+ height: 60px;
+ width: 60px;
+}
+
+SectionTitle {
+ overflow: auto;
+ padding: 5px 20px;
+ color: #000;
+ text-align: center;
+ font-weight: bold;
+}
+
+img {
+ vertical-align:middle;
+}
+
+.right{
+ float:right;
+}
+
+.left{
+ float:left;
+}
+
+Footer{
+ overflow: auto;
+ position:fixed;
+ bottom:0;
+ left:0;
+ width: 100%;
+ background-color: #F9F9F9;
+ font-size: x-small;
+}
\ No newline at end of file
diff --git a/Logs/CoinIcons.json b/Logs/CoinIcons.json
new file mode 100644
index 0000000000000000000000000000000000000000..b4078039fd031e958e9d616f336df60e57449e7c
GIT binary patch
literal 42818
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
Ox%_wbbM{kaKmQL_*gtLn
literal 0
HcmV?d00001
diff --git a/NPlusMiner.ps1 b/NPlusMiner.ps1
index f847d80..9bf43e6 100644
--- a/NPlusMiner.ps1
+++ b/NPlusMiner.ps1
@@ -1,6 +1,6 @@
<#
This file is part of NPlusMiner
-Copyright (c) 2018-2019 MrPlus
+Copyright (c) 2018-2020 MrPlus
NPlusMiner is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@ along with this program. If not, see .
<#
Product: NPlusMiner
File: NPlusMiner.ps1
-version: 5.4.1
-version date: 20190809
+version: 7.3.2
+version date: 20200509
#>
param(
@@ -88,7 +88,7 @@ param(
@"
NPlusMiner
-Copyright (c) 2018-2019 MrPlus
+Copyright (c) 2018-2020 MrPlus
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
@@ -110,7 +110,22 @@ Write-Host -F Yellow " Copyright and license notices must be preserved."
$Global:Config = [hashtable]::Synchronized(@{})
$Global:Config | Add-Member -Force @{ConfigFile = $ConfigFile}
$Global:Variables = [hashtable]::Synchronized(@{})
- $Global:Variables | Add-Member -Force -MemberType ScriptProperty -Name 'StatusText' -Value{ $this._StatusText;$This._StatusText = @() } -SecondValue { If (!$this._StatusText){$this._StatusText=@()};$this._StatusText+=$args[0];$Variables | Add-Member -Force @{RefreshNeeded = $True} }
+ $Global:Variables | Add-Member -Force -MemberType ScriptProperty -Name 'StatusText' -Value{ $this._StatusText;$This._StatusText = [System.Collections.ArrayList]::Synchronized(@()) } -SecondValue { If (!$this._StatusText){$this._StatusText=[System.Collections.ArrayList]::Synchronized(@())};$this._StatusText+=$args[0];$Variables.RefreshNeeded = $True }
+
+ # Set Console size
+ $ConsoleHeight = 50
+ $ConsoleWidth = 160
+ $pswindow = (get-host).ui.rawui
+ $newsize = $pswindow.buffersize
+ # $newsize.height = 3000
+ $newsize.width = $ConsoleWidth
+ $pswindow.buffersize = $newsize
+ $newsize = $pswindow.windowsize
+ # $newsize.height = $ConsoleHeight
+ $newsize.width = $ConsoleWidth
+ $pswindow.windowsize = $newsize
+
+ rv ConsoleHeight, ConsoleWidth, pswindow, newsize
# Load Branding
If (Test-Path ".\Config\Branding.json") {
@@ -155,6 +170,12 @@ Function Global:TimerUITick
If ($true) { #$_.Pool -in ($config.PoolName -replace "24hr","" -replace "plus","")) {
$Variables.EarningsPool = $_.Pool
$Variables.Earnings.($_.Pool) = $_
+ $Variables.Earnings.($_.Pool).PaymentThreshold =
+ If ($Config.PoolsConfig.($_.Pool).PayoutThreshold.($Config.Passwordcurrency)) {
+ $Config.PoolsConfig.($_.Pool).PayoutThreshold.($Config.Passwordcurrency)
+ } Else {
+ $_.PaymentThreshold
+ }
}
}
rv EarnTrack
@@ -165,7 +186,7 @@ Function Global:TimerUITick
(compare -ReferenceObject $CheckedListBoxPools.Items -DifferenceObject ((Get-ChildItem ".\Pools").BaseName | sort -Unique) | ? {$_.SideIndicator -eq "=>"}).InputObject | % { if ($_ -ne $null){}$CheckedListBoxPools.Items.AddRange($_)}
$Config.PoolName | foreach {$CheckedListBoxPools.SetItemChecked($CheckedListBoxPools.Items.IndexOf($_),$True)}
}
- $Variables | Add-Member -Force @{InCycle = $True}
+ $Variables.InCycle = $True
# $MainForm.Number+=1
$MainForm.Text = $Branding.ProductLable + " " + $Variables.CurrentVersion + " Runtime " + ("{0:dd\ \d\a\y\s\ hh\:mm}" -f ((get-date)-$Variables.ScriptStartDate)) + " Path: " + (Split-Path $script:MyInvocation.MyCommand.Path)
$host.UI.RawUI.WindowTitle = $Branding.ProductLable + " " + $Variables.CurrentVersion + " Runtime " + ("{0:dd\ \d\a\y\s\ hh\:mm}" -f ((get-date)-$Variables.ScriptStartDate)) + " Path: " + (Split-Path $script:MyInvocation.MyCommand.Path)
@@ -184,13 +205,13 @@ Function Global:TimerUITick
# https://stackoverflow.com/questions/8466343/why-controls-do-not-want-to-get-removed
If (Test-Path ".\logs\DailyEarnings.csv"){
- $Chart1 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'Front7DaysEarnings' -Width 505 -Height 85"
+ $Chart1 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'Front7DaysEarnings' -Width 505 -Height 85 -Currency $($Config.Passwordcurrency)"
$Chart1.top = 74
$Chart1.left = 0
$RunPage.Controls.Add($Chart1)
$Chart1.BringToFront()
- $Chart2 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'DayPoolSplit' -Width 200 -Height 85"
+ $Chart2 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'DayPoolSplit' -Width 200 -Height 85 -Currency $($Config.Passwordcurrency)"
$Chart2.top = 74
$Chart2.left = 500
$RunPage.Controls.Add($Chart2)
@@ -206,15 +227,15 @@ Function Global:TimerUITick
@{Name="Balance";Expression={$_.Balance}},
# @{Name="Unpaid";Expression={$_.total_unpaid}},
# @{Name="BTC/D";Expression={"{0:N8}" -f ($_.BTCD)}},
- @{Name="1h m$([char]0x20BF)/D";Expression={"{0:N3}" -f ($_.Growth1*1000*24)}},
- @{Name="6h m$([char]0x20BF)/D";Expression={"{0:N3}" -f ($_.Growth6*1000*4)}},
- @{Name="24h m$([char]0x20BF)/D";Expression={"{0:N3}" -f ($_.Growth24*1000)}},
+ @{Name="1h $((Get-DisplayCurrency $_.Growth1 24).UnitStringPerDay)";Expression={(Get-DisplayCurrency $_.Growth1 24).RoundedValue}},
+ @{Name="6h $((Get-DisplayCurrency $_.Growth6 4).UnitStringPerDay)";Expression={(Get-DisplayCurrency $_.Growth6 4).RoundedValue}},
+ @{Name="24h $((Get-DisplayCurrency $_.Growth24).UnitStringPerDay)";Expression={(Get-DisplayCurrency $_.Growth24).RoundedValue}},
@{Name = "Est. Pay Date"; Expression = {if ($_.EstimatedPayDate -is 'DateTime') {$_.EstimatedPayDate.ToShortDateString()} else {$_.EstimatedPayDate}}},
@{Name="PaymentThreshold";Expression={"$($_.PaymentThreshold) ($('{0:P0}' -f $($_.Balance / $_.PaymentThreshold)))"}}#,
# @{Name="Wallet";Expression={$_.Wallet}}
- ) | Sort "1h m$([char]0x20BF)/D","6h m$([char]0x20BF)/D","24h m$([char]0x20BF)/D" -Descending)
+ ) | Sort "1h $((Get-DisplayCurrency $_.Growth1 24).UnitStringPerDay)","6h $((Get-DisplayCurrency $_.Growth6 4).UnitStringPerDay)","24h $((Get-DisplayCurrency $_.Growth24).UnitStringPerDay)" -Descending)
$EarningsDGV.DataSource = [System.Collections.ArrayList]@($DisplayEarnings)
$EarningsDGV.ClearSelection()
}
@@ -227,12 +248,15 @@ Function Global:TimerUITick
@{Name = "Coin"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"$($_.Info)"}}},
@{Name = "Pool"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"$($_.Name)"}}},
@{Name = "Speed"; Expression={$_.HashRates.PSObject.Properties.Value | ForEach {if($_ -ne $null){"$($_ | ConvertTo-Hash)/s"}else{"Benchmarking"}}}},
- @{Name = "mBTC/Day"; Expression={$_.Profits.PSObject.Properties.Value*1000 | ForEach {if($_ -ne $null){$_.ToString("N3")}else{"Benchmarking"}}}},
- @{Name = "BTC/Day"; Expression={$_.Profits.PSObject.Properties.Value | ForEach {if($_ -ne $null){$_.ToString("N5")}else{"Benchmarking"}}}},
- @{Name = "BTC/GH/Day"; Expression={$_.Pools.PSObject.Properties.Value.Price | ForEach {($_*1000000000).ToString("N5")}}}
+ # @{Name = "mBTC/Day"; Expression={$_.Profits.PSObject.Properties.Value | ForEach {if($_ -ne $null){($_*1000).ToString("N3")}else{"Benchmarking"}}}},
+ @{Name = "mBTC/Day"; Expression={(($_.Profits.PSObject.Properties.Value | Measure -Sum).Sum *1000).ToString("N3")}},
+ # @{Name = "BTC/Day"; Expression={$_.Profits.PSObject.Properties.Value | ForEach {if($_ -ne $null){$_.ToString("N5")}else{"Benchmarking"}}}},
+ @{Name = "BTC/Day"; Expression={(($_.Profits.PSObject.Properties.Value | Measure -Sum).Sum).ToString("N3")}},
+ # @{Name = "BTC/GH/Day"; Expression={$_.Pools.PSObject.Properties.Value.Price | ForEach {($_*1000000000).ToString("N15")}}}
+ @{Name = "BTC/GH/Day"; Expression={(($_.Pools.PSObject.Properties.Value.Price | Measure -Sum).Sum *1000000000).ToString("N5")}}
) | sort "mBTC/Day" -Descending)
If ($Config.ShowOnlyTopCoins){
- $EstimationsDGV.DataSource = [System.Collections.ArrayList]@($DisplayEstimations | sort "mBTC/Day" -Descending | Group "Algorithm" | % { $_.Group | select -First 1} | sort "mBTC/Day" -Descending)
+ $EstimationsDGV.DataSource = [System.Collections.ArrayList]@($DisplayEstimations | sort "mBTC/Day" -Descending | Group "Type","Algorithm" | % { $_.Group | select -First 1} | sort "mBTC/Day" -Descending)
} else {
$EstimationsDGV.DataSource = [System.Collections.ArrayList]@($DisplayEstimations)
}
@@ -278,7 +302,17 @@ Function Global:TimerUITick
}
If ($Variables.ActiveMinerPrograms) {
- $RunningMinersDGV.DataSource = [System.Collections.ArrayList]@($Variables.ActiveMinerPrograms | ? {$_.Status -eq "Running"} | select Type,Algorithms,Coin,Name,@{Name="HashRate";Expression={"$($_.HashRate | ConvertTo-Hash)/s"}},@{Name="Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f $_.Active}},@{Name="Total Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f $_.TotalActive}},Host | sort Type)
+ # $RunningMinersDGV.DataSource = [System.Collections.ArrayList]@($Variables.ActiveMinerPrograms | ? {$_.Status -eq "Running"} | select Type,Algorithms,Coin,Name,@{Name="HashRate";Expression={"$($_.HashRate | ConvertTo-Hash)/s"}},@{Name="Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f $_.Active}},@{Name="Total Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f $_.TotalActive}},Host | sort Type)
+ $RunningMinersDGV.DataSource = [System.Collections.ArrayList]@($Variables.ActiveMinerPrograms.Clone() | ? {$_.Status -eq "Running"} | select @(
+ @{Name = "Type";Expression={$_.Type}},
+ @{Name = "Algorithm";Expression={$_.Algorithms}},
+ @{Name = "Coin"; Expression={$_.Coin}},
+ @{Name = "Miner";Expression={$_.Name}},
+ @{Name="HashRate";Expression={"$($_.HashRate | ConvertTo-Hash)/s"}},
+ @{Name ="Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f $_.Active}},
+ @{Name ="Total Active";Expression={"{0:hh}:{0:mm}:{0:ss}" -f $_.TotalActive}},
+ @{Name = "Pool"; Expression={$_.Pools.PSObject.Properties.Value | ForEach {"$($_.Name)"}}} ) | sort Type
+ )
$RunningMinersDGV.ClearSelection()
[Array] $processRunning = $Variables.ActiveMinerPrograms | Where { $_.Status -eq "Running" }
@@ -286,12 +320,13 @@ Function Global:TimerUITick
# Update-Status("No miner running")
}
}
- $LabelBTCPrice.text = If($Variables.Rates.$Currency -gt 0){"BTC/$($Config.Currency) $($Variables.Rates.($Config.Currency))"}
- $Variables | Add-Member -Force @{InCycle = $False}
+ $LabelBTCPrice.text = If($Variables.Rates.$Currency -gt 0){"$($Config.Passwordcurrency)/$($Config.Currency) $($Variables.Rates.($Config.Currency))"}
+ $Variables.InCycle = $False
If ($Variables.Earnings.Values -ne $Null){
- $LabelBTCD.Text = "Avg: " +("{0:N6}" -f ($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum) + " $([char]0x20BF)/D | " + ("{0:N3}" -f (($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum*1000)) + " m$([char]0x20BF)/D"
+ $LabelBTCD.Text = "Avg: " + ((Get-DisplayCurrency ($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum)).DisplayStringPerDay
+
$LabelEarningsDetails.Lines = @()
# If ((($Variables.Earnings.Values | measure -Property Growth1 -Sum).sum*1000*24) -lt ((($Variables.Earnings.Values | measure -Property BTCD -Sum).sum*1000)*0.999)) {
@@ -304,7 +339,7 @@ Function Global:TimerUITick
{$_ -lt 0}
{"<"}
}
- $LabelEarningsDetails.Lines += "Last 1h: " + ("{0:N3}" -f (($Variables.Earnings.Values | measure -Property Growth1 -Sum).sum*1000*24)) + " m$([char]0x20BF)/D " + $TrendSign
+ $LabelEarningsDetails.Lines += "Last 1h: " + ((Get-DisplayCurrency ($Variables.Earnings.Values | measure -Property Growth1 -Sum).sum 24)).DisplayStringPerDay + " " + $TrendSign
$TrendSign = switch ([Math]::Round((($Variables.Earnings.Values | measure -Property Growth6 -Sum).sum*1000*4),3) - [Math]::Round((($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum*1000),3)) {
{$_ -eq 0}
{"="}
@@ -313,7 +348,7 @@ Function Global:TimerUITick
{$_ -lt 0}
{"<"}
}
- $LabelEarningsDetails.Lines += "Last 6h: " + ("{0:N3}" -f (($Variables.Earnings.Values | measure -Property Growth6 -Sum).sum*1000*4)) + " m$([char]0x20BF)/D " + $TrendSign
+ $LabelEarningsDetails.Lines += "Last 6h: " + ((Get-DisplayCurrency ($Variables.Earnings.Values | measure -Property Growth6 -Sum).sum 4)).DisplayStringPerDay + " " + $TrendSign
$TrendSign = switch ([Math]::Round((($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum*1000),3) - [Math]::Round((($Variables.Earnings.Values | measure -Property BTCD -Sum).sum*1000*0.96),3)) {
{$_ -eq 0}
{"="}
@@ -322,7 +357,7 @@ Function Global:TimerUITick
{$_ -lt 0}
{"<"}
}
- $LabelEarningsDetails.Lines += "Last 24h: " + ("{0:N3}" -f (($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum*1000)) + " m$([char]0x20BF)/D " + $TrendSign
+ $LabelEarningsDetails.Lines += "Last 24h: " + ((Get-DisplayCurrency ($Variables.Earnings.Values | measure -Property Growth24 -Sum).sum)).DisplayStringPerDay + " " + $TrendSign
rv TrendSign
} else {
$LabelBTCD.Text = "Waiting data from pools."
@@ -332,9 +367,9 @@ Function Global:TimerUITick
if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
if (!(IsLoaded(".\Includes\Core.ps1"))) {. .\Includes\Core.ps1;RegisterLoaded(".\Includes\Core.ps1")}
- $Variables | Add-Member -Force @{CurrentProduct = (Get-Content .\Version.json | ConvertFrom-Json).Product}
- $Variables | Add-Member -Force @{CurrentVersion = [Version](Get-Content .\Version.json | ConvertFrom-Json).Version}
- $Variables | Add-Member -Force @{CurrentVersionAutoUpdated = (Get-Content .\Version.json | ConvertFrom-Json).AutoUpdated.Value}
+ $Variables.CurrentProduct = (Get-Content .\Version.json | ConvertFrom-Json).Product
+ $Variables.CurrentVersion = [Version](Get-Content .\Version.json | ConvertFrom-Json).Version
+ $Variables.CurrentVersionAutoUpdated = (Get-Content .\Version.json | ConvertFrom-Json).AutoUpdated.Value
if ((Get-Content .\Version.json | ConvertFrom-Json).AutoUpdated -and $LabelNotifications.Lines[$LabelNotifications.Lines.Count-1] -ne "Auto Updated on $($Variables.CurrentVersionAutoUpdated)"){
$LabelNotifications.ForeColor = "Green"
Update-Notifications("Running $($Variables.CurrentProduct) Version $([Version]$Variables.CurrentVersion)")
@@ -391,7 +426,7 @@ Function Global:TimerUITick
# ) | Out-Host
If ($Config.ShowOnlyTopCoins){
- $DisplayEstimations | sort "mBTC/Day" -Descending | Group "Algorithm" | % { $_.Group | select -First 1} | sort Type,"mBTC/Day" -Descending | Format-Table -GroupBy Type | Out-Host
+ $DisplayEstimations | sort "mBTC/Day" -Descending | Group "Type","Algorithm" | % { $_.Group | select -First 1} | sort Type,"mBTC/Day" -Descending | Format-Table -GroupBy Type | Out-Host
} else {
$DisplayEstimations | sort Type,"mBTC/Day" -Descending | Format-Table -GroupBy Type | Out-Host
}
@@ -509,12 +544,23 @@ Function PrepareWriteConfig{
$Config | Add-Member -Force @{$_.Tag = [Int]$_.Text}
}
$Config | Add-Member -Force @{$CheckedListBoxPools.Tag = $CheckedListBoxPools.CheckedItems}
+
+ $ServermodePageControls | ? {(($_.gettype()).Name -eq "CheckBox")} | foreach {$Config | Add-Member -Force @{$_.Tag = $_.Checked}}
+ $ServermodePageControls | ? {(($_.gettype()).Name -eq "TextBox")} | foreach {$Config | Add-Member -Force @{$_.Tag = $_.Text}}
$MonitoringSettingsControls | ? {(($_.gettype()).Name -eq "CheckBox")} | foreach {$Config | Add-Member -Force @{$_.Tag = $_.Checked}}
$MonitoringSettingsControls | ? {(($_.gettype()).Name -eq "TextBox")} | foreach {$Config | Add-Member -Force @{$_.Tag = $_.Text}}
Write-Config -ConfigFile $ConfigFile -Config $Config
$Config = Load-Config -ConfigFile $ConfigFile
+
+ $ServerPasswd = ConvertTo-SecureString $Config.Server_Password -AsPlainText -Force
+ $ServerCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_User, $ServerPasswd)
+ $Variables.ServerCreds = $ServerCreds
+ $ServerClientPasswd = ConvertTo-SecureString $Config.Server_ClientPassword -AsPlainText -Force
+ $ServerClientCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_ClientUser, $ServerClientPasswd)
+ $Variables.ServerClientCreds = $ServerClientCreds
+
$MainForm.Refresh
# [windows.forms.messagebox]::show("Please restart NPlusMiner",'Config saved','ok','Information') | out-null
}
@@ -653,9 +699,9 @@ $SelGPUDSTM = $Config.SelGPUDSTM
$SelGPUCC = $Config.SelGPUCC
$MainForm | Add-Member -Name "Variables" -Value $Variables -MemberType NoteProperty -Force
-$Variables | Add-Member -Force @{CurrentProduct = (Get-Content .\Version.json | ConvertFrom-Json).Product}
-$Variables | Add-Member -Force @{CurrentVersion = [Version](Get-Content .\Version.json | ConvertFrom-Json).Version}
-$Variables | Add-Member -Force @{CurrentVersionAutoUpdated = (Get-Content .\Version.json | ConvertFrom-Json).AutoUpdated.Value}
+$Variables.CurrentProduct = (Get-Content .\Version.json | ConvertFrom-Json).Product
+$Variables.CurrentVersion = [Version](Get-Content .\Version.json | ConvertFrom-Json).Version
+$Variables.CurrentVersionAutoUpdated = (Get-Content .\Version.json | ConvertFrom-Json).AutoUpdated.Value
$Variables.StatusText = "Idle"
$TabControl = New-object System.Windows.Forms.TabControl
@@ -674,6 +720,8 @@ $MonitoringPage = New-Object System.Windows.Forms.TabPage
$MonitoringPage.Text = "Monitoring"
$EstimationsPage = New-Object System.Windows.Forms.TabPage
$EstimationsPage.Text = "Benchmarks"
+$ServerModePage = New-Object System.Windows.Forms.TabPage
+$ServerModePage.Text = "Server Mode"
$tabControl.DataBindings.DefaultDataSourceUpdateMode = 0
$tabControl.Location = New-Object System.Drawing.Point(10,91)
@@ -681,7 +729,7 @@ $tabControl.Name = "tabControl"
$tabControl.width = 720
$tabControl.height = 359
$MainForm.Controls.Add($tabControl)
-$TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $MonitoringPage, $EstimationsPage))
+$TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $MonitoringPage, $EstimationsPage, $ServerModePage))
# Form Controls
$MainFormControls = @()
@@ -700,7 +748,7 @@ $TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $Monitori
$LabelEarningsDetails.MultiLine = $true
$LabelEarningsDetails.text = ""
$LabelEarningsDetails.AutoSize = $false
- $LabelEarningsDetails.width = 200 #382
+ $LabelEarningsDetails.width = 382 #200
$LabelEarningsDetails.height = 47 #62
$LabelEarningsDetails.location = New-Object System.Drawing.Point(57,2)
$LabelEarningsDetails.Font = 'lucida console,10'
@@ -813,13 +861,13 @@ $TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $Monitori
$RunPageControls += $LabelEarnings
If (Test-Path ".\logs\DailyEarnings.csv"){
- $Chart1 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'Front7DaysEarnings' -Width 505 -Height 85"
+ $Chart1 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'Front7DaysEarnings' -Width 505 -Height 85 -Currency $($Config.Passwordcurrency)"
$Chart1.top = 74
$Chart1.left = 2
$RunPageControls += $Chart1
}
If (Test-Path ".\logs\DailyEarnings.csv"){
- $Chart2 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'DayPoolSplit' -Width 200 -Height 85"
+ $Chart2 = Invoke-Expression -Command ".\Includes\Charting.ps1 -Chart 'DayPoolSplit' -Width 200 -Height 85 -Currency $($Config.Passwordcurrency)"
$Chart2.top = 74
$Chart2.left = 500
$RunPageControls += $Chart2
@@ -854,10 +902,21 @@ $TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $Monitori
$LabelCopyright.Size = New-Object System.Drawing.Size(200,20)
$LabelCopyright.LinkColor = "BLUE"
$LabelCopyright.ActiveLinkColor = "BLUE"
- $LabelCopyright.Text = "Copyright (c) 2018-2019 MrPlus"
+ $LabelCopyright.Text = "Copyright (c) 2018-2020 MrPlus"
$LabelCopyright.add_Click({[system.Diagnostics.Process]::start("https://github.com/MrPlusGH/NPlusMiner/blob/master/LICENSE")})
$RunPageControls += $LabelCopyright
+ $LabelWebUI = New-Object System.Windows.Forms.LinkLabel
+ # $LabelWebUI.Location = New-Object System.Drawing.Size(415,61)
+ # $LabelWebUI.Size = New-Object System.Drawing.Size(200,20)
+ $LabelWebUI.Location = New-Object System.Drawing.Size(250,246)
+ $LabelWebUI.Size = New-Object System.Drawing.Size(200,20)
+ $LabelWebUI.LinkColor = "BLUE"
+ $LabelWebUI.ActiveLinkColor = "BLUE"
+ $LabelWebUI.Text = "Web interface"
+ $LabelWebUI.add_Click({[system.Diagnostics.Process]::start("http://$($Config.Server_ClientIP):$($Config.Server_ClientPort)/Status")})
+ $RunPageControls += $LabelWebUI
+
$LabelRunningMiners = New-Object system.Windows.Forms.Label
$LabelRunningMiners.text = "Running Miners"
$LabelRunningMiners.AutoSize = $false
@@ -968,6 +1027,202 @@ $TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $Monitori
# $DblBuff = ($EstimationsDGV.GetType()).GetProperty("DoubleBuffered", ('Instance','NonPublic'))
# $DblBuff.SetValue($MainForm, $Truen, $null)
+# Server mode Page Controls
+ $ServermodePageControls = @()
+
+ $CheckBoxStandalone = New-Object system.Windows.Forms.CheckBox
+ $CheckBoxStandalone.Tag = "Server_Standalone"
+ $CheckBoxStandalone.text = "Standalone"
+ $CheckBoxStandalone.AutoSize = $false
+ $CheckBoxStandalone.width = 200
+ $CheckBoxStandalone.height = 20
+ $CheckBoxStandalone.location = New-Object System.Drawing.Point(2,2)
+ $CheckBoxStandalone.Font = 'Microsoft Sans Serif,10'
+ $CheckBoxStandalone.Checked = $Config.Server_Standalone
+ $ServermodePageControls += $CheckBoxStandalone
+
+ $CheckBoxBeAServer = New-Object system.Windows.Forms.CheckBox
+ $CheckBoxBeAServer.Tag = "Server_On"
+ $CheckBoxBeAServer.text = "Be a server"
+ $CheckBoxBeAServer.AutoSize = $false
+ $CheckBoxBeAServer.width = 140
+ $CheckBoxBeAServer.height = 20
+ $CheckBoxBeAServer.location = New-Object System.Drawing.Point(2,24)
+ $CheckBoxBeAServer.Font = 'Microsoft Sans Serif,10'
+ $CheckBoxBeAServer.Checked = $Config.Server_On
+ $ServermodePageControls += $CheckBoxBeAServer
+
+ $LabelServerPort = New-Object system.Windows.Forms.Label
+ $LabelServerPort.text = "Server Port"
+ $LabelServerPort.AutoSize = $false
+ $LabelServerPort.width = 120
+ $LabelServerPort.height = 20
+ $LabelServerPort.location = New-Object System.Drawing.Point(2,48)
+ $LabelServerPort.Font = 'Microsoft Sans Serif,10'
+ # $LabelServerPort.Enabled = $CheckBoxBeAServer.Checked
+ $ServermodePageControls += $LabelServerPort
+
+ $TBServerPort = New-Object system.Windows.Forms.TextBox
+ $TBServerPort.Tag = "Server_Port"
+ $TBServerPort.MultiLine = $False
+ # $TBServerPort.Scrollbars = "Vertical"
+ $TBServerPort.text = $Config.Server_Port
+ $TBServerPort.AutoSize = $false
+ $TBServerPort.width = 50
+ $TBServerPort.height = 20
+ $TBServerPort.location = New-Object System.Drawing.Point(122,48)
+ $TBServerPort.Font = 'Microsoft Sans Serif,10'
+ # $TBServerPort.Enabled = $CheckBoxBeAServer.Checked
+ $ServermodePageControls += $TBServerPort
+
+ $LabelServerUser = New-Object system.Windows.Forms.Label
+ $LabelServerUser.text = "Server User"
+ $LabelServerUser.AutoSize = $false
+ $LabelServerUser.width = 120
+ $LabelServerUser.height = 20
+ $LabelServerUser.location = New-Object System.Drawing.Point(2,72)
+ $LabelServerUser.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $LabelServerUser
+
+ $TBServerUser = New-Object system.Windows.Forms.TextBox
+ $TBServerUser.Tag = "Server_User"
+ $TBServerUser.MultiLine = $False
+ # $TBServerIP.Scrollbars = "Vertical"
+ $TBServerUser.text = $Config.Server_User
+ $TBServerUser.AutoSize = $false
+ $TBServerUser.width = 150
+ $TBServerUser.height = 20
+ $TBServerUser.location = New-Object System.Drawing.Point(122,72)
+ $TBServerUser.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $TBServerUser
+
+ $LabelServerPassword = New-Object system.Windows.Forms.Label
+ $LabelServerPassword.text = "Server Password"
+ $LabelServerPassword.AutoSize = $false
+ $LabelServerPassword.width = 120
+ $LabelServerPassword.height = 20
+ $LabelServerPassword.location = New-Object System.Drawing.Point(274,72)
+ $LabelServerPassword.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $LabelServerPassword
+
+ $TBServerPassword = New-Object system.Windows.Forms.TextBox
+ $TBServerPassword.Tag = "Server_Password"
+ $TBServerPassword.MultiLine = $False
+ # $TBServerPassword.Scrollbars = "Vertical"
+ $TBServerPassword.text = if ($Config.Server_Password) {$Config.Server_Password} else {(New-Guid).guid}
+ $TBServerPassword.AutoSize = $false
+ $TBServerPassword.width = 150
+ $TBServerPassword.height = 20
+ $TBServerPassword.location = New-Object System.Drawing.Point(396,72)
+ $TBServerPassword.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $TBServerPassword
+
+ $CheckBoxServerClient = New-Object system.Windows.Forms.CheckBox
+ $CheckBoxServerClient.Tag = "Server_Client"
+ $CheckBoxServerClient.text = "Use a server"
+ $CheckBoxServerClient.AutoSize = $false
+ $CheckBoxServerClient.width = 140
+ $CheckBoxServerClient.height = 20
+ $CheckBoxServerClient.location = New-Object System.Drawing.Point(2,94)
+ $CheckBoxServerClient.Font = 'Microsoft Sans Serif,10'
+ $CheckBoxServerClient.Checked = $Config.Server_Client
+ $ServermodePageControls += $CheckBoxServerClient
+
+ $LabelServerClientIP = New-Object system.Windows.Forms.Label
+ $LabelServerClientIP.text = "Server IP"
+ $LabelServerClientIP.AutoSize = $false
+ $LabelServerClientIP.width = 120
+ $LabelServerClientIP.height = 20
+ $LabelServerClientIP.location = New-Object System.Drawing.Point(2,116)
+ $LabelServerClientIP.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $LabelServerClientIP
+
+ $TBServerClientIP = New-Object system.Windows.Forms.TextBox
+ $TBServerClientIP.Tag = "Server_ClientIP"
+ $TBServerClientIP.MultiLine = $False
+ # $TBServerClientIP.Scrollbars = "Vertical"
+ $TBServerClientIP.text = $Config.Server_ClientIP
+ $TBServerClientIP.AutoSize = $false
+ $TBServerClientIP.width = 150
+ $TBServerClientIP.height = 20
+ $TBServerClientIP.location = New-Object System.Drawing.Point(122,116)
+ $TBServerClientIP.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $TBServerClientIP
+
+ $LabelServerClientPort = New-Object system.Windows.Forms.Label
+ $LabelServerClientPort.text = "Port"
+ $LabelServerClientPort.AutoSize = $false
+ $LabelServerClientPort.width = 120
+ $LabelServerClientPort.height = 20
+ $LabelServerClientPort.location = New-Object System.Drawing.Point(274,116)
+ $LabelServerClientPort.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $LabelServerClientPort
+
+ $TBServerClientPort = New-Object system.Windows.Forms.TextBox
+ $TBServerClientPort.Tag = "Server_ClientPort"
+ $TBServerClientPort.MultiLine = $False
+ # $TBServerClientPort.Scrollbars = "Vertical"
+ $TBServerClientPort.text = $Config.Server_ClientPort
+ $TBServerClientPort.AutoSize = $false
+ $TBServerClientPort.width = 50
+ $TBServerClientPort.height = 20
+ $TBServerClientPort.location = New-Object System.Drawing.Point(396,116)
+ $TBServerClientPort.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $TBServerClientPort
+
+ $LabelServerClientUser = New-Object system.Windows.Forms.Label
+ $LabelServerClientUser.text = "Server User"
+ $LabelServerClientUser.AutoSize = $false
+ $LabelServerClientUser.width = 120
+ $LabelServerClientUser.height = 20
+ $LabelServerClientUser.location = New-Object System.Drawing.Point(2,138)
+ $LabelServerClientUser.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $LabelServerClientUser
+
+ $TBServerClientUser = New-Object system.Windows.Forms.TextBox
+ $TBServerClientUser.Tag = "Server_ClientUser"
+ $TBServerClientUser.MultiLine = $False
+ # $TBServerClientUser.Scrollbars = "Vertical"
+ $TBServerClientUser.text = $Config.Server_ClientUser
+ $TBServerClientUser.AutoSize = $false
+ $TBServerClientUser.width = 150
+ $TBServerClientUser.height = 20
+ $TBServerClientUser.location = New-Object System.Drawing.Point(122,138)
+ $TBServerClientUser.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $TBServerClientUser
+
+ $LabelServerClientPassword = New-Object system.Windows.Forms.Label
+ $LabelServerClientPassword.text = "Server Password"
+ $LabelServerClientPassword.AutoSize = $false
+ $LabelServerClientPassword.width = 120
+ $LabelServerClientPassword.height = 20
+ $LabelServerClientPassword.location = New-Object System.Drawing.Point(274,138)
+ $LabelServerClientPassword.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $LabelServerClientPassword
+
+ $TBServerClientPassword = New-Object system.Windows.Forms.TextBox
+ $TBServerClientPassword.Tag = "Server_ClientPassword"
+ $TBServerClientPassword.MultiLine = $False
+ # $TBServerClientPassword.Scrollbars = "Vertical"
+ $TBServerClientPassword.text = $Config.Server_ClientPassword
+ $TBServerClientPassword.AutoSize = $false
+ $TBServerClientPassword.width = 150
+ $TBServerClientPassword.height = 20
+ $TBServerClientPassword.location = New-Object System.Drawing.Point(396,138)
+ $TBServerClientPassword.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $TBServerClientPassword
+
+ $ButtonWriteServerConfig = New-Object system.Windows.Forms.Button
+ $ButtonWriteServerConfig.text = "Save Config"
+ $ButtonWriteServerConfig.width = 100
+ $ButtonWriteServerConfig.height = 30
+ $ButtonWriteServerConfig.location = New-Object System.Drawing.Point(610,300)
+ $ButtonWriteServerConfig.Font = 'Microsoft Sans Serif,10'
+ $ServermodePageControls += $ButtonWriteServerConfig
+
+ $ButtonWriteServerConfig.Add_Click({PrepareWriteConfig})
+
+
# Config Page Controls
$ConfigPageControls = @()
@@ -1141,7 +1396,7 @@ $TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $Monitori
$ConfigPageControls += $TBCurrency
$LabelPwdCurrency = New-Object system.Windows.Forms.Label
- $LabelPwdCurrency.text = "Pwd Currency"
+ $LabelPwdCurrency.text = "Payout Currency"
$LabelPwdCurrency.AutoSize = $false
$LabelPwdCurrency.width = 120
$LabelPwdCurrency.height = 20
@@ -1275,7 +1530,7 @@ $TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $Monitori
$StartPort = 4068
Update-Status("Finding available TCP Port for $($This.Text)")
$Port = Get-FreeTcpPort($StartPort)
- $Variables | Add-Member -Force @{"$($This.Text)MinerAPITCPPort" = $Port}
+ $Variables."$($This.Text)MinerAPITCPPort" = $Port
Update-Status("Miners API Port: $($Port)")
$StartPort = $Port+1
}
@@ -1301,7 +1556,7 @@ $TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $Monitori
$StartPort = 4068
Update-Status("Finding available TCP Port for $($This.Text)")
$Port = Get-FreeTcpPort($StartPort)
- $Variables | Add-Member -Force @{"$($This.Text)MinerAPITCPPort" = $Port}
+ $Variables."$($This.Text)MinerAPITCPPort" = $Port
Update-Status("Miners API Port: $($Port)")
$StartPort = $Port+1
}
@@ -1327,7 +1582,7 @@ $TabControl.Controls.AddRange(@($RunPage, $SwitchingPage, $ConfigPage, $Monitori
$StartPort = 4068
Update-Status("Finding available TCP Port for $($This.Text)")
$Port = Get-FreeTcpPort($StartPort)
- $Variables | Add-Member -Force @{"$($This.Text)MinerAPITCPPort" = $Port}
+ $Variables."$($This.Text)MinerAPITCPPort" = $Port
Update-Status("Miners API Port: $($Port)")
$StartPort = $Port + 1
}
@@ -1723,7 +1978,7 @@ $ButtonPause.Add_Click( {
$LabelBTCD.Text = "Mining Paused | $($Branding.ProductLable) $($Variables.CurrentVersion)"
If ($Variables.DonationRunning) {
- $Variables | Add-Member -Force @{ DonationRunning = $False }
+ $Variables.DonationRunning = $False
$ConfigLoad = Get-Content $Config.ConfigFile | ConvertFrom-json
$ConfigLoad | % {$_.psobject.properties | sort Name | % {$Config | Add-Member -Force @{$_.Name = $_.Value}}}
$Config | Add-Member -Force -MemberType ScriptProperty -Name "PoolsConfig" -Value {
@@ -1746,7 +2001,7 @@ $ButtonPause.Add_Click( {
else {
$Variables.Paused = $False
$ButtonPause.Text = "Pause"
- $Variables | Add-Member -Force @{LastDonated = (Get-Date).AddDays(-1).AddHours(1)}
+ $Variables.LastDonated = (Get-Date).AddDays(-1).AddHours(1)
$TimerUI.Start()
# Stop and start mining to immediately switch to unpaused state without waiting for current sleep to finish
@@ -1779,7 +2034,7 @@ $ButtonStart.Add_Click( {
# $TimerUI.Interval = 1000
If ($Variables.DonationRunning) {
- $Variables | Add-Member -Force @{ DonationRunning = $False }
+ $Variables.DonationRunning = $False
$ConfigLoad = Get-Content $Config.ConfigFile | ConvertFrom-json
$ConfigLoad | % {$_.psobject.properties | sort Name | % {$Config | Add-Member -Force @{$_.Name = $_.Value}}}
$Config | Add-Member -Force -MemberType ScriptProperty -Name "PoolsConfig" -Value {
@@ -1805,8 +2060,8 @@ $ButtonStart.Add_Click( {
PrepareWriteConfig
$ButtonStart.Text = "Stop"
InitApplication
- $Variables | add-Member -Force @{MainPath = (Split-Path $script:MyInvocation.MyCommand.Path)}
- $Variables | Add-Member -Force @{LastDonated = (Get-Date).AddDays(-1).AddHours(1)}
+ $Variables.MainPath = (Split-Path $script:MyInvocation.MyCommand.Path)
+ $Variables.LastDonated = (Get-Date).AddDays(-1).AddHours(1)
Start-IdleTracking
@@ -1836,6 +2091,7 @@ $MainForm.controls.AddRange($MainFormControls)
$RunPage.controls.AddRange(@($RunPageControls))
$SwitchingPage.controls.AddRange(@($SwitchingPageControls))
$EstimationsPage.Controls.AddRange(@($EstimationsDGV))
+$ServerModePage.Controls.AddRange($ServermodePageControls)
$ConfigPage.controls.AddRange($ConfigPageControls)
$GroupMonitoringSettings.Controls.AddRange($MonitoringSettingsControls)
$MonitoringPage.controls.AddRange($MonitoringPageControls)
diff --git a/Pools/ahashpool.ps1 b/Pools/ahashpool.ps1
new file mode 100644
index 0000000..3d15c76
--- /dev/null
+++ b/Pools/ahashpool.ps1
@@ -0,0 +1,51 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "https://www.ahashpool.com/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = ".mine.ahashpool.com"
+# $PriceField = "actual_last24h"
+$PriceField = "estimate_current"
+$DivisorMultiplier = 1000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($_)$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = "$ahashpool_Coin $ahashpool_Coinname"
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/ahashpool24hr.ps1 b/Pools/ahashpool24hr.ps1
new file mode 100644
index 0000000..515414b
--- /dev/null
+++ b/Pools/ahashpool24hr.ps1
@@ -0,0 +1,51 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "https://www.ahashpool.com/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = ".mine.ahashpool.com"
+$PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+$DivisorMultiplier = 1000000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($_)$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = "$ahashpool_Coin $ahashpool_Coinname"
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/ahashpoolplus.ps1 b/Pools/ahashpoolplus.ps1
new file mode 100644
index 0000000..062ca66
--- /dev/null
+++ b/Pools/ahashpoolplus.ps1
@@ -0,0 +1,57 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+Try {
+ $dtAlgos = New-Object System.Data.DataTable
+ if (Test-Path ((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\ahashpoolplus\ahashpoolplus.xml")) {
+ $dtAlgos.ReadXml((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\ahashpoolplus\ahashpoolplus.xml") | out-null
+ }
+}
+catch { return }
+
+if (-not $dtAlgos) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = ".mine.ahashpool.com"
+$PriceField = "Plus_Price"
+# $PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+$DivisorMultiplier = 1000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$dtAlgos | foreach {
+ $Pool = $_
+ $PoolHost = "$($Pool.algo)$($HostSuffix)"
+ $PoolPort = $Pool.port
+ $PoolAlgorithm = Get-Algorithm $Pool.algo
+
+ $Divisor = $DivisorMultiplier * [Double]$Pool.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Pool.$PriceField / $Divisor * (1 - ($Pool.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Pool.$PriceField / $Divisor * (1 - ($Pool.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = "Auto-($($Pool.symbol))"
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ Coin = "Auto-($($Pool.symbol))"
+ }
+ }
+}
diff --git a/Pools/blazepool.ps1 b/Pools/blazepool.ps1
new file mode 100644
index 0000000..0d16dd7
--- /dev/null
+++ b/Pools/blazepool.ps1
@@ -0,0 +1,51 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "http://api.blazepool.com/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = ".mine.blazepool.com"
+# $PriceField = "actual_last24h"
+$PriceField = "estimate_current"
+$DivisorMultiplier = 1000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($_)$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = ""
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/blazepool24hr.ps1 b/Pools/blazepool24hr.ps1
new file mode 100644
index 0000000..9e70a60
--- /dev/null
+++ b/Pools/blazepool24hr.ps1
@@ -0,0 +1,51 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "http://api.blazepool.com/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = ".mine.blazepool.com"
+$PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+$DivisorMultiplier = 1000000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($_)$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = ""
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/blazepoolplus.ps1 b/Pools/blazepoolplus.ps1
new file mode 100644
index 0000000..e029f65
--- /dev/null
+++ b/Pools/blazepoolplus.ps1
@@ -0,0 +1,57 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+Try {
+ $dtAlgos = New-Object System.Data.DataTable
+ if (Test-Path ((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\blazepoolplus\blazepoolplus.xml")) {
+ $dtAlgos.ReadXml((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\blazepoolplus\blazepoolplus.xml") | out-null
+ }
+}
+catch { return }
+
+if (-not $dtAlgos) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = ".mine.blazepool.com"
+$PriceField = "Plus_Price"
+# $PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+$DivisorMultiplier = 1000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$dtAlgos | foreach {
+ $Pool = $_
+ $PoolHost = "$($Pool.algo)$($HostSuffix)"
+ $PoolPort = $Pool.port
+ $PoolAlgorithm = Get-Algorithm $Pool.algo
+
+ $Divisor = $DivisorMultiplier * [Double]$Pool.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Pool.$PriceField / $Divisor * (1 - ($Pool.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Pool.$PriceField / $Divisor * (1 - ($Pool.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = "Auto($($Pool.symbol))"
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ Coin = "Auto-($($Pool.symbol))"
+ }
+ }
+}
diff --git a/Pools/blockmasters.ps1 b/Pools/blockmasters.ps1
new file mode 100644
index 0000000..e9ce1c1
--- /dev/null
+++ b/Pools/blockmasters.ps1
@@ -0,0 +1,61 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "http://blockmasters.co/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = "blockmasters.co"
+# $PriceField = "actual_last24h"
+$PriceField = "estimate_current"
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = 1000000 * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ $Locations = "eu.", ""
+ $Locations | ForEach-Object {
+ $Pool_Location = $_
+
+ switch ($Pool_Location) {
+ "eu." {$Location = "EU"}
+ "" {$Location = "US"}
+ }
+ $PoolHost = "$($Pool_Location)$($HostSuffix)"
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = "$ahashpool_Coin $ahashpool_Coinname"
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+ }
+}
diff --git a/Pools/blockmasters24hr.ps1 b/Pools/blockmasters24hr.ps1
new file mode 100644
index 0000000..1821bd9
--- /dev/null
+++ b/Pools/blockmasters24hr.ps1
@@ -0,0 +1,62 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "http://blockmasters.co/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = "blockmasters.co"
+$PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+$DivisorMultiplier = 1000000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ $Locations = "eu.", ""
+ $Locations | ForEach-Object {
+ $Pool_Location = $_
+
+ switch ($Pool_Location) {
+ "eu." {$Location = "EU"}
+ "" {$Location = "US"}
+ }
+ $PoolHost = "$($Pool_Location)$($HostSuffix)"
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = "$ahashpool_Coin $ahashpool_Coinname"
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+ }
+}
diff --git a/Pools/blockmastersplus.ps1 b/Pools/blockmastersplus.ps1
new file mode 100644
index 0000000..244a4d5
--- /dev/null
+++ b/Pools/blockmastersplus.ps1
@@ -0,0 +1,70 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+Try {
+ $dtAlgos = New-Object System.Data.DataTable
+ if (Test-Path ((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\blockmastersplus\blockmastersplus.xml")) {
+ $dtAlgos.ReadXml((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\blockmastersplus\blockmastersplus.xml") | out-null
+ }
+}
+catch { return }
+
+if (-not $dtAlgos) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = "blockmasters.co"
+$PriceField = "Plus_Price"
+# $PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+$DivisorMultiplier = 1000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$dtAlgos | foreach {
+ $Pool = $_
+ $PoolHost = "$($HostSuffix)"
+ $PoolPort = $Pool.port
+ $PoolAlgorithm = Get-Algorithm $Pool.algo
+
+ $Divisor = $DivisorMultiplier * [Double]$Pool.mbtc_mh_factor
+
+ $Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Pool.$PriceField / $Divisor * (1 - ($Pool.fees / 100)))
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ $PoolPassword = If ( ! $Config.PartyWhenAvailable ) {"$($WorkerName),c=$($PwdCurr)"} else { "$($WorkerName),c=$($PwdCurr),m=PARTY.NPlusMiner" }
+ $PoolPassword = If ( $Pool.symbol ) { "$($PoolPassword),mc=$($Pool.symbol)" } else { $PoolPassword }
+
+ $Locations = "eu.", ""
+ $Locations | ForEach-Object {
+ $Pool_Location = $_
+
+ switch ($Pool_Location) {
+ "eu." {$Location = "EU"}
+ default {$Location = "US"}
+ }
+ $PoolHost = "$($Pool_Location)$($HostSuffix)"
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = $Pool.symbol
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = $PoolPassword
+ Location = $Location
+ SSL = $false
+ Coin = $Pool.symbol
+ }
+ }
+ }
+}
diff --git a/Pools/miningpoolhub.ps1 b/Pools/miningpoolhub.ps1
new file mode 100644
index 0000000..d13e464
--- /dev/null
+++ b/Pools/miningpoolhub.ps1
@@ -0,0 +1,65 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try { $Request = Invoke-ProxiedWebRequest "https://miningpoolhub.com/index.php?page=api&action=getautoswitchingandprofitsstatistics" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request.success) {
+ return
+}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+
+$Locations = 'EU', 'US', 'Asia'
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Fee = 0.0090 + (0.0003 / .01) #Fee + Withdrawal fee based on 0.01 BTC threshold
+$Divisor = 1000000000
+
+
+ $Request.return | ForEach-Object {
+ $Current = $_
+ $Algorithm = Get-Algorithm($_.algo -replace "-")
+ $Coin = $_.current_mining_coin_symbol
+
+ $Stat = Set-Stat -Name "$($Name)_$($Algorithm)_Profit" -Value ([decimal]$_.profit / $Divisor * (1 - $Fee))
+ $Price = (($Stat.Live * (1 - [Math]::Min($Stat.Day_Fluctuation, 1))) + ($Stat.Day * (0 + [Math]::Min($Stat.Day_Fluctuation, 1))))
+
+$Locations | ForEach-Object {
+ $Location = $_
+
+ [PSCustomObject]@{
+ Algorithm = $Algorithm
+ Info = $Coin
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ Protocol = 'stratum+tcp'
+ Host = $Current.all_host_list.split(";") | Sort-Object -Descending {$_ -ilike "$Location*"} | Select-Object -First 1
+ Port = $Current.algo_switch_port
+ User = "$($PoolConf.UserName).$($PoolConf.WorkerName.replace('ID=',''))"
+ Pass = 'x'
+ Location = $Location
+ SSL = $false
+ Coin = $Coin
+ }
+
+ [PSCustomObject]@{
+ Algorithm = $Algorithm
+ Info = $Coin
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ Protocol = 'stratum+ssl'
+ Host = $Current.all_host_list.split(";") | Sort-Object -Descending {$_ -ilike "$Location*"} | Select-Object -First 1
+ Port = $Current.algo_switch_port
+ User = "$($PoolConf.UserName).$($PoolConf.WorkerName.replace('ID=',''))"
+ Pass = 'x'
+ Location = $Location
+ SSL = $true
+ coin = $Coin
+ }
+ }
+}
+
+
diff --git a/Pools/nicehash.ps1 b/Pools/nicehash.ps1
new file mode 100644
index 0000000..7438311
--- /dev/null
+++ b/Pools/nicehash.ps1
@@ -0,0 +1,79 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "https://api2.nicehash.com/main/api/v2/public/simplemultialgo/info/" | ConvertFrom-Json
+ $RequestAlgodetails = Invoke-ProxiedWebRequest "https://api2.nicehash.com/main/api/v2/mining/algorithms/" | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request -or -not $RequestAlgodetails) {return}
+
+$Request.miningAlgorithms | foreach {$Algo = $_.Algorithm ; $_ | Add-Member -force @{algodetails = $RequestAlgodetails.miningAlgorithms | ? {$_.Algorithm -eq $Algo}}}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$Fees = 5
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+
+ $Request.miningAlgorithms| ? { [Double]$_.paying -gt 0 } | ForEach-Object {
+ $Algo = $_.Algorithm
+ $NiceHash_Port = $_.algodetails.port
+ $NiceHash_Algorithm = Get-Algorithm $_.Algorithm
+ $NiceHash_Coin = ""
+
+ $DivisorMultiplier = 100000
+ $Divisor = $DivisorMultiplier * [Double]$_.Algodetails.marketFactor
+ $Divisor = 100000000
+
+ $Stat = Set-Stat -Name "$($Name)_$($NiceHash_Algorithm)_Profit" -Value ([Double]$_.paying / $Divisor * (1 - ($Fees / 100)))
+
+$Locations = "eu", "usa", "hk", "jp", "in", "br"
+$Locations | ForEach-Object {
+ $NiceHash_Location = $_
+
+ switch ($NiceHash_Location) {
+ "eu" {$Location = "EU"}
+ "usa" {$Location = "US"}
+ "jp" {$Location = "JP"}
+ "hk" {$Location = "JP"}
+ "in" {$Location = "JP"}
+ # "br" {$Location = "US"}
+ }
+ $NiceHash_Host = "$($Algo).$($NiceHash_Location).nicehash.com"
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $NiceHash_Algorithm
+ Info = $NiceHash_Coin
+ Price = $Stat."Minute_5"*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $NiceHash_Host
+ Port = $NiceHash_Port
+ User = "$($PoolConf.Wallet).$($PoolConf.WorkerName.Replace('ID=',''))"
+ Pass = "x"
+ Location = $Location
+ SSL = $false
+ }
+
+ [PSCustomObject]@{
+ Algorithm = $NiceHash_Algorithm
+ Info = $NiceHash_Coin
+ Price = $Stat."Minute_5"*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+ssl"
+ Host = $NiceHash_Host
+ Port = $NiceHash_Port
+ User = "$($PoolConf.Wallet).$($PoolConf.WorkerName.Replace('ID=',''))"
+ Pass = "x"
+ Location = $Location
+ SSL = $true
+ }
+ }
+ }
+}
diff --git a/Pools/nlpool.ps1 b/Pools/nlpool.ps1
new file mode 100644
index 0000000..e9c0544
--- /dev/null
+++ b/Pools/nlpool.ps1
@@ -0,0 +1,57 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1; RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "http://www.nlpool.nl/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = "mine.nlpool.nl"
+# $PriceField = "actual_last24h"
+$PriceField = "estimate_current"
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+$ConfName = if ($Config.PoolsConfig.$Name -ne $Null) {$Name}else {"default"}
+$PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = $HostSuffix
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = 1000000 * [Double]$Request.$_.mbtc_mh_factor
+
+ switch ($PoolAlgorithm) {
+ # "equihash125" { $Divisor *= 2 } #temp fix
+ # "equihash144" { $Divisor *= 2 } #temp fix
+ # "equihash192" { $Divisor *= 2 } #temp fix
+ "verushash" { $Divisor *= 2 } #temp fix
+ }
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = ""
+ Price = $Stat.Live * $PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/nlpool24hr.ps1 b/Pools/nlpool24hr.ps1
new file mode 100644
index 0000000..d6e8ec0
--- /dev/null
+++ b/Pools/nlpool24hr.ps1
@@ -0,0 +1,57 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "http://www.nlpool.nl/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = "mine.nlpool.nl"
+$PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = $HostSuffix
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = 1000000000 * [Double]$Request.$_.mbtc_mh_factor
+
+ switch ($PoolAlgorithm) {
+ # "equihash125" { $Divisor *= 2 } #temp fix
+ # "equihash144" { $Divisor *= 2 } #temp fix
+ # "equihash192" { $Divisor *= 2 } #temp fix
+ "verushash" { $Divisor *= 2 } #temp fix
+ }
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = ""
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/nlpoolplus.ps1 b/Pools/nlpoolplus.ps1
new file mode 100644
index 0000000..7b32cb5
--- /dev/null
+++ b/Pools/nlpoolplus.ps1
@@ -0,0 +1,62 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+Try {
+ $dtAlgos = New-Object System.Data.DataTable
+ if (Test-Path ((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\nlpoolplus\nlpoolplus.xml")) {
+ $dtAlgos.ReadXml((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\nlpoolplus\nlpoolplus.xml") | out-null
+ }
+}
+catch { return }
+
+if (-not $dtAlgos) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = "mine.nlpool.nl"
+$PriceField = "Plus_Price"
+# $PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$dtAlgos | foreach {
+ $Pool = $_
+ $PoolHost = $HostSuffix
+ $PoolPort = $Pool.port
+ $PoolAlgorithm = Get-Algorithm $Pool.algo
+
+ $Divisor = 1000000 * [Double]$Pool.mbtc_mh_factor
+
+ switch ($PoolAlgorithm) {
+ # "equihash125" { $Divisor *= 2 } #temp fix
+ # "equihash144" { $Divisor *= 2 } #temp fix
+ # "equihash192" { $Divisor *= 2 } #temp fix
+ "verushash" { $Divisor *= 2 } #temp fix
+ }
+
+ $Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Pool.$PriceField / $Divisor * (1 - ($Pool.fees / 100)))
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = "Auto($($Pool.symbol))"
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ Coin = "Auto-($($Pool.symbol))"
+ }
+ }
+}
diff --git a/Pools/prohashing.ps1 b/Pools/prohashing.ps1
new file mode 100644
index 0000000..a122ba2
--- /dev/null
+++ b/Pools/prohashing.ps1
@@ -0,0 +1,59 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Headers = @{"Accept"="text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"}
+ $Request = ((Invoke-ProxiedWebRequest "https://prohashing.com/api/v1/status" -UseBasicParsing -Headers $Headers).Content | ConvertFrom-Json).data
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = "prohashing.com"
+# $PriceField = "actual_last24h"
+$PriceField = "estimate_current"
+$DivisorMultiplier = 1
+
+# + 2.9% supplementary fee for conversion
+# Makes 2 + 2.9 = 4.9%
+# There is 0.00015 BTC fee on withdraw as well (Estimation 0.00015/0.0025 = 6%) Using 0.0025 as most pools do use this Payout threshold
+# Makes 2 + 2.9 + 6 = 10.9% !!!
+# Taking 1.5 here considerinf withdraw at 0.01BTC
+# $Request.$_.fees = $Request.$_.fees + 2.9 + 1.5
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.pps_fee)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.pps_fee)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = ""
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = "$($PoolConf.UserName)"
+ Pass = "a=$($PoolAlgorithm),n=$($PoolConf.WorkerName.replace('ID=',''))"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/prohashing24hr.ps1 b/Pools/prohashing24hr.ps1
new file mode 100644
index 0000000..1d127ce
--- /dev/null
+++ b/Pools/prohashing24hr.ps1
@@ -0,0 +1,59 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Headers = @{"Accept"="text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"}
+ $Request = ((Invoke-ProxiedWebRequest "https://prohashing.com/api/v1/status" -UseBasicParsing -Headers $Headers).Content | ConvertFrom-Json).data
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = "prohashing.com"
+# $PriceField = "actual_last24h"
+$PriceField = "actual_last24h"
+$DivisorMultiplier = 1
+
+# + 2.9% supplementary fee for conversion
+# Makes 2 + 2.9 = 4.9%
+# There is 0.00015 BTC fee on withdraw as well (Estimation 0.00015/0.0025 = 6%) Using 0.0025 as most pools do use this Payout threshold
+# Makes 2 + 2.9 + 6 = 10.9% !!!
+# Taking 1.5 here considerinf withdraw at 0.01BTC
+# $Request.$_.fees = $Request.$_.fees + 2.9 + 1.5
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.pps_fee)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.pps_fee)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = ""
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = "$($PoolConf.UserName)"
+ Pass = "a=$($PoolAlgorithm),n=$($PoolConf.WorkerName.replace('ID=',''))"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/prohashingplus.ps1 b/Pools/prohashingplus.ps1
new file mode 100644
index 0000000..efe5d4f
--- /dev/null
+++ b/Pools/prohashingplus.ps1
@@ -0,0 +1,66 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+Try {
+ $dtAlgos = New-Object System.Data.DataTable
+ if (Test-Path ((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\prohashingPlus\prohashingPlus.xml")) {
+ $dtAlgos.ReadXml((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\prohashingPlus\prohashingPlus.xml") | out-null
+ }
+}
+catch { return }
+
+if (-not $dtAlgos) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = "prohashing.com"
+$PriceField = "Plus_Price"
+# $PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+$DivisorMultiplier = 1
+
+# + 2.9% supplementary fee for conversion
+# Makes 2 + 2.9 = 4.9%
+# There is 0.00015 BTC fee on withdraw as well (Estimation 0.00015/0.0025 = 6%) Using 0.0025 as most pools do use this Payout threshold
+# Makes 2 + 2.9 + 6 = 10.9% !!!
+# $Request.$_.fees = $Request.$_.fees + 2.9 + 6
+# Taking 1.5 here considerinf withdraw at 0.01BTC
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$dtAlgos | foreach {
+ $PoolHost = "$($HostSuffix)"
+ $PoolPort = $_.port
+ $PoolAlgorithm = Get-Algorithm $_.algo
+
+ $Divisor = $DivisorMultiplier * [Double]$_.mbtc_mh_factor
+
+ $Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$_.$PriceField / $Divisor * (1 - (($_.fees))))
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ $PoolPassword = If ( ! $Config.PartyWhenAvailable ) {"$($WorkerName),c=$($PwdCurr)"} else { "$($WorkerName),c=$($PwdCurr),m=party.NPlusMiner" }
+ $PoolPassword = If ( $_.symbol) { "$($PoolPassword),mc=$($_.symbol)" } else { $PoolPassword }
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = $_.symbol
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor #*$SoloPenalty
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = "$($PoolConf.UserName)"
+ Pass = "a=$($PoolAlgorithm),n=$($PoolConf.WorkerName.replace('ID=',''))"
+ Location = $Location
+ SSL = $false
+ Coin = "Auto ($($_.symbol))"
+ SoloBlocksPenalty = $_.SoloBlocksPenalty
+ }
+ }
+}
diff --git a/Pools/zergpool.ps1 b/Pools/zergpool.ps1
new file mode 100644
index 0000000..dbe2d16
--- /dev/null
+++ b/Pools/zergpool.ps1
@@ -0,0 +1,51 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "http://api.zergpool.com:8080/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = ".mine.zergpool.com"
+# $PriceField = "actual_last24h"
+$PriceField = "estimate_current"
+$DivisorMultiplier = 1000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($_)$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = ""
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/zergpool24hr.ps1 b/Pools/zergpool24hr.ps1
new file mode 100644
index 0000000..a19a2c4
--- /dev/null
+++ b/Pools/zergpool24hr.ps1
@@ -0,0 +1,51 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+try {
+ $Request = Invoke-ProxiedWebRequest "http://api.zergpool.com:8080/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+}
+catch { return }
+
+if (-not $Request) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = ".mine.zergpool.com"
+$PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+$DivisorMultiplier = 1000000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
+ $PoolHost = "$($_)$($HostSuffix)"
+ $PoolPort = $Request.$_.port
+ $PoolAlgorithm = Get-Algorithm $Request.$_.name
+
+ $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
+
+ if ((Get-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit") -eq $null) {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+ else {$Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))}
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = ""
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = "$($WorkerName),c=$($PwdCurr)"
+ Location = $Location
+ SSL = $false
+ }
+ }
+}
diff --git a/Pools/zergpoolplus.ps1 b/Pools/zergpoolplus.ps1
new file mode 100644
index 0000000..99fbbf4
--- /dev/null
+++ b/Pools/zergpoolplus.ps1
@@ -0,0 +1,59 @@
+if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
+
+Try {
+ $dtAlgos = New-Object System.Data.DataTable
+ if (Test-Path ((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\zergpoolplus\zergpoolplus.xml")) {
+ $dtAlgos.ReadXml((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\zergpoolplus\zergpoolplus.xml") | out-null
+ }
+}
+catch { return }
+
+if (-not $dtAlgos) {return}
+
+$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
+$HostSuffix = ".mine.zergpool.com"
+$PriceField = "Plus_Price"
+# $PriceField = "actual_last24h"
+# $PriceField = "estimate_current"
+$DivisorMultiplier = 1000000
+
+$Location = "US"
+
+# Placed here for Perf (Disk reads)
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $PoolConf = $Config.PoolsConfig.$ConfName
+
+$dtAlgos | foreach {
+ $PoolHost = "$($_.algo)$($HostSuffix)"
+ $PoolPort = $_.port
+ $PoolAlgorithm = Get-Algorithm $_.algo
+
+ $Divisor = $DivisorMultiplier * [Double]$_.mbtc_mh_factor
+
+ $Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$_.$PriceField / $Divisor * (1 - ($_.fees / 100)))
+
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+
+ $PoolPassword = If ( ! $Config.PartyWhenAvailable ) {"$($WorkerName),c=$($PwdCurr)"} else { "$($WorkerName),c=$($PwdCurr),m=party.NPlusMiner" }
+ $PoolPassword = If ( $_.symbol) { "$($PoolPassword),mc=$($_.symbol)" } else { $PoolPassword }
+
+ if ($PoolConf.Wallet) {
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = $_.symbol
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor #*$SoloPenalty
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = $PoolPassword
+ Location = $Location
+ SSL = $false
+ Coin = $_.symbol
+ SoloBlocksPenalty = $_.SoloBlocksPenalty
+ }
+ }
+}
diff --git a/Pools/zpool.ps1 b/Pools/zpool.ps1
index 0d4ca0c..c9b358c 100644
--- a/Pools/zpool.ps1
+++ b/Pools/zpool.ps1
@@ -1,7 +1,7 @@
if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
try {
- $Request = Invoke-WebRequest "http://www.zpool.ca/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+ $Request = Invoke-ProxiedWebRequest "http://www.zpool.ca/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
}
catch { return }
@@ -14,34 +14,34 @@ $PriceField = "estimate_current"
$DivisorMultiplier = 1000000
# Placed here for Perf (Disk reads)
- $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
$PoolConf = $Config.PoolsConfig.$ConfName
$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
- $Algo = $_
+ $Algo = $_
$PoolHost = "$($_)$($HostSuffix)"
$PoolPort = $Request.$_.port
$PoolAlgorithm = Get-Algorithm $Request.$_.name
$Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
- $Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))
-
+ $Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))
+
$PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
$WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
- $Locations = "eu", "na", "sea"
- $Locations | ForEach-Object {
- $Pool_Location = $_
-
- switch ($Pool_Location) {
- "eu" {$Location = "EU"}
- "na" {$Location = "US"}
- "sea" {$Location = "JP"}
- default {$Location = "US"}
- }
- $PoolHost = "$($Algo).$($Pool_Location)$($HostSuffix)"
+ $Locations = "eu", "na", "sea"
+ $Locations | ForEach-Object {
+ $Pool_Location = $_
+
+ switch ($Pool_Location) {
+ "eu" {$Location = "EU"}
+ "na" {$Location = "US"}
+ "sea" {$Location = "JP"}
+ default {$Location = "US"}
+ }
+ $PoolHost = "$($Algo).$($Pool_Location)$($HostSuffix)"
if ($PoolConf.Wallet) {
[PSCustomObject]@{
diff --git a/Pools/zpool24hr.ps1 b/Pools/zpool24hr.ps1
index f8b8703..1b4dc24 100644
--- a/Pools/zpool24hr.ps1
+++ b/Pools/zpool24hr.ps1
@@ -1,7 +1,7 @@
if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
try {
- $Request = Invoke-WebRequest "http://www.zpool.ca/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
+ $Request = Invoke-ProxiedWebRequest "http://www.zpool.ca/api/status" -UseBasicParsing -Headers @{"Cache-Control" = "no-cache"} | ConvertFrom-Json
}
catch { return }
@@ -14,12 +14,12 @@ $PriceField = "actual_last24h"
$DivisorMultiplier = 1000000000
# Placed here for Perf (Disk reads)
- $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
$PoolConf = $Config.PoolsConfig.$ConfName
$Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
- $Algo = $_
+ $Algo = $_
$PoolHost = "$($_)$($HostSuffix)"
$PoolPort = $Request.$_.port
$PoolAlgorithm = Get-Algorithm $Request.$_.name
@@ -32,17 +32,17 @@ $DivisorMultiplier = 1000000000
$PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
$WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
- $Locations = "eu", "na", "sea"
- $Locations | ForEach-Object {
- $Pool_Location = $_
-
- switch ($Pool_Location) {
- "eu" {$Location = "EU"}
- "na" {$Location = "US"}
- "sea" {$Location = "JP"}
- default {$Location = "US"}
- }
- $PoolHost = "$($Algo).$($Pool_Location)$($HostSuffix)"
+ $Locations = "eu", "na", "sea"
+ $Locations | ForEach-Object {
+ $Pool_Location = $_
+
+ switch ($Pool_Location) {
+ "eu" {$Location = "EU"}
+ "na" {$Location = "US"}
+ "sea" {$Location = "JP"}
+ default {$Location = "US"}
+ }
+ $PoolHost = "$($Algo).$($Pool_Location)$($HostSuffix)"
if ($PoolConf.Wallet) {
[PSCustomObject]@{
diff --git a/Pools/zpoolplus.ps1 b/Pools/zpoolplus.ps1
index 7311536..db1a442 100644
--- a/Pools/zpoolplus.ps1
+++ b/Pools/zpoolplus.ps1
@@ -1,61 +1,73 @@
if (!(IsLoaded(".\Includes\include.ps1"))) {. .\Includes\include.ps1;RegisterLoaded(".\Includes\include.ps1")}
Try {
- $Request = get-content ((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\zpoolplus\zpoolplus.json") | ConvertFrom-Json
+ $dtAlgos = New-Object System.Data.DataTable
+ if (Test-Path ((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\zpoolplus\zpoolplus.xml")) {
+ $dtAlgos.ReadXml((split-path -parent (get-item $script:MyInvocation.MyCommand.Path).Directory) + "\BrainPlus\zpoolplus\zpoolplus.xml") | out-null
+ }
}
catch { return }
-if (-not $Request) {return}
+if (-not $dtAlgos) {return}
$Name = (Get-Item $script:MyInvocation.MyCommand.Path).BaseName
$HostSuffix = ".mine.zpool.ca"
-$PriceField = "actual_last24h"
+$PriceField = "Plus_Price"
+# $PriceField = "actual_last24h"
# $PriceField = "estimate_current"
$DivisorMultiplier = 1000000
+
+$Location = "US"
# Placed here for Perf (Disk reads)
- $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
+ $ConfName = if ($Config.PoolsConfig.$Name -ne $Null){$Name}else{"default"}
$PoolConf = $Config.PoolsConfig.$ConfName
-
-
- $Request | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
- $Algo = $_
- $PoolPort = $Request.$_.port
- $PoolAlgorithm = Get-Algorithm $Request.$_.name
-
- $Divisor = $DivisorMultiplier * [Double]$Request.$_.mbtc_mh_factor
- $Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Request.$_.$PriceField / $Divisor * (1 - ($Request.$_.fees / 100)))
+$dtAlgos | foreach {
+ $Pool = $_
+ $PoolHost = "$($Pool.algo)$($HostSuffix)"
+ $PoolPort = $Pool.port
+ $PoolAlgorithm = Get-Algorithm $Pool.algo
+
+ $Divisor = $DivisorMultiplier * [Double]$Pool.mbtc_mh_factor
- $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
- $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+ $Stat = Set-Stat -Name "$($Name)_$($PoolAlgorithm)_Profit" -Value ([Double]$Pool.$PriceField / $Divisor * (1 - ($Pool.fees / 100)))
- $Locations = "eu", "na", "sea"
- $Locations | ForEach-Object {
- $Pool_Location = $_
- switch ($Pool_Location) {
- "eu" {$Location = "EU"}
- "na" {$Location = "US"}
- "sea" {$Location = "JP"}
- default {$Location = "US"}
- }
- $PoolHost = "$($Algo).$($Pool_Location)$($HostSuffix)"
+ $PwdCurr = if ($PoolConf.PwdCurrency) {$PoolConf.PwdCurrency}else {$Config.Passwordcurrency}
+ $WorkerName = If ($PoolConf.WorkerName -like "ID=*") {$PoolConf.WorkerName} else {"ID=$($PoolConf.WorkerName)"}
+ $PoolPassword = If ( ! $Config.PartyWhenAvailable ) {"$($WorkerName),c=$($PwdCurr)"} else { "$($WorkerName),c=$($PwdCurr),m=party.NPlusMiner" }
+ $PoolPassword = If ( $Pool.symbol ) { "$($PoolPassword),zap=$($Pool.symbol)" } else { $PoolPassword }
+
+ $Locations = "eu", "na", "sea", "jp"
+ $Locations | ForEach-Object {
+ $Pool_Location = $_
+
+ switch ($Pool_Location) {
+ "eu" {$Location = "EU"}
+ "na" {$Location = "US"}
+ "sea" {$Location = "JP"}
+ "jp" {$Location = "JP"}
+ default {$Location = "US"}
+ }
+ $PoolHost = "$($Pool.algo).$($Pool_Location)$($HostSuffix)"
+
if ($PoolConf.Wallet) {
- [PSCustomObject]@{
- Algorithm = $PoolAlgorithm
- Info = "$ahashpool_Coin $ahashpool_Coinname"
- Price = $Stat.Live*$PoolConf.PricePenaltyFactor
- StablePrice = $Stat.Week
- MarginOfError = $Stat.Week_Fluctuation
- Protocol = "stratum+tcp"
- Host = $PoolHost
- Port = $PoolPort
- User = $PoolConf.Wallet
- Pass = "$($WorkerName),c=$($PwdCurr)"
- Location = $Location
- SSL = $false
- }
+ [PSCustomObject]@{
+ Algorithm = $PoolAlgorithm
+ Info = $Pool.symbol
+ Price = $Stat.Live*$PoolConf.PricePenaltyFactor #*$SoloPenalty
+ StablePrice = $Stat.Week
+ MarginOfError = $Stat.Week_Fluctuation
+ Protocol = "stratum+tcp"
+ Host = $PoolHost
+ Port = $PoolPort
+ User = $PoolConf.Wallet
+ Pass = $PoolPassword
+ Location = $Location
+ SSL = $false
+ Coin = $Pool.symbol
+ }
}
}
}
diff --git a/README.md b/README.md
index edad22b..3fbcf42 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,28 @@
-Copyright (c) 2018 MrPlus (https://github.com/MrPlusGH/NPlusMiner)
+Copyright (c) 2018-2019 MrPlus (https://github.com/MrPlusGH/NPlusMiner)
-Copyright (c) 2018 Nemo (https://github.com/nemosminer/NemosMiner)
+Copyright (c) 2018 Nemo (https://github.com/Minerx117/NemosMiner)
-![alt text](https://www.zpool.ca/images/logoz.png)
- # NPlusMiner ZPool Edition
+
+
+
+
+ # NPlusMiner-v7.4.2 - NVIDIA | AMD | CPU
- Readme Updated 2019 March 1
+ Readme Updated 2020 May 19
-[![Version tag](https://img.shields.io/github/release/MRPlusGH/NPlusMiner.svg)](https://github.com/MrPlusGH/NPlusMiner/releases/latest) [![Version date tag](https://img.shields.io/github/release-date/MRPlusGH/NPlusMiner.svg)](https://github.com/MRPlusGH/NPlusMiner/releases/latest)
+[![Version tag](https://img.shields.io/github/release/MRPlusGH/NPlusMiner.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAAAAACPAi4CAAACpklEQVRYw+3XO2/TUBQH8PBB+ALMrEisSBAhEbUdGMsCQlEH1KELEoK1iC7AwsbA6JCGvgRCrSBVByKaVIEqqE2dRGkepGkeJLEPPufa12/7JkWiQpzBN77H/kV/+caxIxBSU5GQ+g+EA2H1ZwClSdXVPtb3WR1ajhlQ9xffL+jHtDgwlKlKI4A9iVWibwI96naN3Z/6IdKBE5DrJiD98AVy/oDcM4EtX2AjAKgoHEj0fACewAuQmxyQCj5ALhCQd3l7U2sk4lol7cBGMLDD25J2xjwutQUbYCbwBraptY6bfU8gx/suoGoCdNRHT2Cd913ASZ0DzQRuOx4AJUhVvYFR2QA6n3D73QOg7/5S8wagw4ED3H7wAChB1Q+AYwMYvMXh1AVQgneqLzAs6QB8xiHvAlgC8AXghAFtOMThvQtgCQIAlS2kMrAMcw5ATxAAAFvKhT6kcbzjAChBBoIA9mMqVNQijjMOgBIciwBya4gZbtkBI4EIIA+23YCRIABYvnYF63o0ejOqjRcRuBSLYsWmp69i700gsBgJrWdnBZbOOZB9gnfB+NIi1kL8Mp5x4yXtvXiMnUev9kDgMnbo7pS1X0ZaRTX6UYcDA5w4SlqBCjZWVUEAWjizZQUy2PgKooBa0WZyVmANG3VhAPqYIYbAHAHfKAGIA9DQpm4jMEtA2pJADFC0e/QsAjMd7K5YEogBeI++i8BU0ZlAEICafI+AHSPB7pjAqMSA1JEjgSgAbQZIWUcCYQDuM2DTkUAceMCAZJESNMYH5hkg0d/NGowP5JdfP334XHvisicQB7QVnedPNI2JAGim9PNXYTJASetAZkIAyjpQmRQAuoTSSjcAABWLPZXSo71iBU4bWG3r1NB8ATgnbyx/F7gQXOHAWd8K/wHgN3+TQF05kYYwAAAAAElFTkSuQmCC)](https://github.com/MrPlusGH/NPlusMiner/releases/latest) [![Version date tag](https://img.shields.io/github/release-date/MRPlusGH/NPlusMiner.svg)](https://github.com/MRPlusGH/NPlusMiner/releases/latest) [![Downloads](https://img.shields.io/github/downloads/MrPlusGH/NPlusMiner/total.svg)](https://img.shields.io/github/downloads/MrPlusGH/NPlusMiner/total.svg)
[![GitHub license](https://img.shields.io/github/license/MRPlusGH/NPlusMiner.svg)](https://github.com/MRPlusGH/NPlusMiner/blob/master/LICENSE)
-[![Discord tag](https://img.shields.io/discord/522832112311599124.svg?label=Discord&style=plastic)](https://discord.gg/2BCqPxe) [Click to Join Discord](https://discord.gg/2BCqPxe)
+[![Discord tag](https://img.shields.io/discord/522832112311599124.svg?label=Discord&style=popout&logo=discord)](https://discord.gg/2BCqPxe)
+
+[Click to Join Discord](https://discord.gg/2BCqPxe)
+
+
+
+
+
+
+
*****
**Have questions? Need help?** We're on Discord: https://discord.gg/2BCqPxe
@@ -18,17 +30,233 @@ Copyright (c) 2018 Nemo (https://github.com/nemosminer/NemosMiner)
**For more help and HowTos please check our wiki here** : https://github.com/MrPlusGH/NPlusMiner-Documentation/wiki
BitcoinTalk : https://bitcointalk.org/index.php?topic=2965976.0
*****
+NPlusMiner Monitors mining pools in real-time in order to find the most profitable Algo
+
+ GUI and easy configuration
+ Auto Benchmarks Each algo to get optimal speeds
+ Fully automated
+ Auto Downloads Miners
+ Tracks and display earnings accross pools
+ AutoUpdate
+ Monitoring
+
+*****
-For details on NPlusMiner please refer to https://github.com/MrPlusGH/NPlusMiner/blob/master/README.md
-![alt text](https://raw.githubusercontent.com/MrPlusGH/NPlusMiner-ZPool-Edition/master/Utils/screenshot.jpg)
+Easy configuration, easy start in two steps:
+
+ Run NPLusMiner
+
+ 1. Enter your BTC address and hit Save Config
+ 2. Hit "Start"
+Fee:
+
+ There is a 16 minutes per day fee (1%).
+ Devs are doing their best to make NPlusminer your best tool.
+ We have a fair fee distribution system to contributors.
*****
-Any donations will be much appreciated:
+## Features list
+
+ AI
+
+ NPlusMiner provides deep data analysis to lead to the best mining decisions.
+ BrainPlus is the Core brain computing these calculations and criteria.
+ Not only this does analyze prices, but aglos/coins performances or orphans rate as well.
+
+ GUI
+
+ Since version 2.0 NPlusMiner has a GUI making it easy to configure and run.
+ Relies on config files. No need to edit bat files. Simply run NPlusMiner
+ Set your wallet address and hit start
+ For console lovers. Run NPlusMiner-ConsoleUp.
+
+ AutoUpdate
+
+ Since version 4.0 NPlusMiner integrates an AutoUpdate feature.
+
+ Auto Ban miners
+
+ There are cases where some miners might fail in some systems.
+ I such cases, NPlusMiner will ignore this miner after a count of failure.
+ Default value for max failure is 3 and can be changes in Config.json.
+ "MaxMinerFailure": 3 - set to 0 to deactivate autoban.
+ Bans are only valid for a session. NPlusMiner will retry the miner on restart.
+
+ Pause Mining
+
+ Ability to pause miners while keeping other jobs running (pause button)
+ This will stop mining activity
+ BrainPlus will still run in the background avoiding the learning phase on resume
+ EarningTracker will still run in the background avoiding the learning phase on resume
+
+ prerun
+
+ Ability to run a batch prior switching to a specific algo.
+ For example, can be used to set per algo OC via nvidiaInspector
+ Simply create a file named .bat in prerun folder
+ If .bat does not exist, will try to launch prerun/default.bat
+ Use overclock with caution
+
+ Per pools config (Advanced)
+
+ - **This is for advanced users. Do not use if you do not know what you are doing.**
+ - You can now set specific options per pool. For example, you can mine NiceHash on the internal wallet and other pools on a valid wallet. This configuration is provided as an example in Config\PoolsConfig-NHInternal.json
+ - Available options
+ - Wallet = your wallet address
+ - UserName = your MPH user name
+ - WorkerName = your worker name
+ - PricePenaltyFactor = See explanation below
+ - Algorithm = List of included or excluded Aglo on pool (see example files)
+
+ - Usage
+ - The file Config\PoolsConfig.json contains per pool configuration details. If a pool is listed in this file,
+ the specific settings will be taken into account. If not, the setting for the entry name default will be used.
+ **Do not delete the default entry.**
+ - Edit Config\PoolsConfig.json
+ - Add an entry for the pool you want to customize
+ - The name must be the NPlusMiner name for the pool. ie. for ahashpool, if you use Plus. The name is ahashpoolplus.
+ - (**careful with json formating ;)**)
+ - Best way is to duplicate the default entry
+ - Note that the GUI only updates the default entry. Any other changes need to be done manualy
+
+ PricePenaltyFactor (Advanced)
+
+ - When using advanced per pool configuration, it is possible to add a penalty factor for a specific pool. This simply adds as a multiplicator on estimations presented by the pool.
+ - Example scenario
+ - NiceHash as a 4% fee - Set PricePenaltyFactor to 0.96 (1-0.04)
+ - You feel like a pool is exaggerating his estimations by 10% - Set PricePenaltyFactor to 0.9
+
+ BrainPlus - ahashpoolplus / zergpoolplus / zpoolplus / blazepoolplus / BlockMastersPlus / PhiPhiPoolPlus / StarPoolPlus / HashRefineryPlus
+
+ Did we say AI ;)
+ Uses calculations based on 24hractual and currentestimate ahashpool prices to get more realistic estimate.
+ Includes some trust index based on past 1hr currentestimate variation from 24hr.
+ AND is NOT sensible to spikes.
+ This shows less switching than following Current Estimate and more switching that following the 24hr Actual.
+ Better profitability.
+
+ Pools variants
+
+ 24hr - uses last 24hour Actual API too request profit
+ -Low switching rate
+ plus - uses advanced calculations to maximize profit (AI)
+ -**Best switching rate**
+ normal - uses current estimate API too request profit
+ -High switching rate
+
+ Developers and Contributors fee distribution
+
+ There is a 16 minutes per day fee (1%)
+
+ We use a fair fee distribution to developers and contributors. Fees are distibuted randomly
+ to a public list of devs which can be found here: http://tiny.cc/r355qy
+
+ We want to stay completely transparent on the way fees are managed in the product.
+ Fees cycle occurs once every 24 hours for the selected amount of time (8 minutes).
+ The first donation sequence occurs 1 hour after miners are started.
+ If Interval is set higher than the donation time, the interval will prime.
+ Example for default parameters:
+ Miners started at 10, First donation cycle runs at 11 untill 11:16, Next donation cycle occurs 24 hours after.
+ All donation time and addresses are recorded in the logs folder.
+
+ Miners Monitoring
+
+ Keep tabs on all your mining rigs from one place
+ **Thanks to @NemosMiner for giving is aggreement to share the NemosMiner monitoring servers.**
+ You can now optionally monitor all your workers remotely, both in the GUI and via https://nemosminer.com
+ Monitoring setup instructions https://nemosminer.com/setup.php
+
+ NPlusMiner does not send any personnal informations to servers. Only miner related info are collected as miner names and hashrates. Miners path are all expressed relative so we have no risk to send any personnal informations like username.
-nemo = 1QGADhdMRpp9Pk5u5zG1TrHKRrdK5R81TE
+ Algo selection
+
+ Users might use the Algo list in config to Include or Exclude algos.
+ The list simply works with a +/- system.
+
+ +algo for algo selection
+ -algo for algo removal
+
+ If "+" Used, all selected algo have to be listed
+ If "Minus" Used, all algo selected but exluded ones.
+
+ Do not combine + and - for the same algo
+
+ Examples:
+ Mine anything but x16r: Algo list = -x16r
+ Mine anything but x16r and bcd: Algo list = -x16r,-bcd
+ Mine only x16r: Algo list = +x16r
+ Mine only x16r and BCD: Algo list = +x16r,+bcd
+ Mine any available algo at pool: Algo list =
+
+ Earnings Tracking
+
+ Graphical displays BTC/H and BTC/D as well a estimation of when the pool payment threshold will be reached.
+ Supported pools:
+ ahashpool
+ zergpool
+ zpool
+ nicehash
+ miningpoolhub (partial)
+ Blazepool
+ BlockMasters
+ PhiPhipool
+ Starpool
+ HashRefinery
+ If mining more that one pools, shows stats for any supported pool
+ Press key e in the console window to show/hide earnings
+
+ Support running multiple instances
+
+ **Experimental**
+ More than one instance of NPlusMiner can run on the same rig
+ Each instance must be placed in it's own directory
+ Miner has to be started prior the launch of the next instance
+
+ Optional miners (Advanced)
+
+ Some miners are not enabled by default in NPlusMiner for a variety of reasons
+ A new folder can be found called "OptionalMiners" containing .ps1 files for some miners
+ For advanced users, refer to OptionalMiners\Readme.txt on how to use
+
+ Algo switching log
+
+ Simple algo switching log in csv switching.log file found in Logs folder.
+ You can easily track switching rate.
+
+ Console Display Options
+
+ Use -UIStyle Light or -UIStyle Full in config.json
+ Full = Usual display
+ Light = Show only currently mining info (Default)
+ UIStyle automaticaly swtiches to Full during benchmarking.
+
+ In session console display toggle
+
+ Press key s in the window to switch between light and full display
+ Press key e in the window to show/hide earnings
+ Will toggle display at next refresh
+
+*****
+
+
+If you have Windows 8, or 8.1, please update PowerShell:
+https://www.microsoft.com/en-us/download/details.aspx?id=50395
+
+Some miners may need 'Visual C++ 2013' if you don't already have it: (install both x86 & x64) Visual C++ Redistributable for Visual Studio 2012/2013: https://www.microsoft.com/en-US/download/details.aspx?id=40784
+
+Some miners may need 'Visual C++ 2015' if you don't already have it: (install both x86 & x64) Visual C++ Redistributable for Visual Studio 2014/2015: https://www.microsoft.com/en-US/download/details.aspx?id=48145
+
+Some miners may need 'Visual C++ 2015 update 3' if you don't already have it: (install both x86 & x64) Visual C++ Redistributable for Visual Studio 2015 update 3: https://www.microsoft.com/en-us/download/details.aspx?id=53587
+
+running multiple cards its recommended to increase Virtual Memory 64gb is optimal
+
+Requires Nvidia driver 431.86: https://www.geforce.com/drivers
+
+Made For & Tested with 6x1070 6x1070ti 6x1080 6x1080ti 9x1660ti 6x2060 6x2070 6x2080 6x2080ti(users have reported up to 12cards working have not tested myself) Some miners do not support more that 9 cards
+
+*****
-MrPlus = 134bw4oTorEJUUVFhokDQDfNqTs7rBMNYy
Licensed under the GNU General Public License v3.0
Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. https://github.com/mrplusgh/NPlusMiner/blob/master/LICENSE
diff --git a/Utils/API-Mine.ps1 b/Utils/API-Mine.ps1
new file mode 100644
index 0000000..3f518ff
--- /dev/null
+++ b/Utils/API-Mine.ps1
@@ -0,0 +1,14 @@
+
+Write-Host "Pausing NPlusMiner via API"
+
+. .\Includes\Include.ps1
+
+$Variables = [hashtable]::Synchronized(@{})
+$ConfigFile = ".\Config\Config.json"
+$Config = Load-Config -ConfigFile $ConfigFile
+
+$ServerPasswd = ConvertTo-SecureString $Config.Server_Password -AsPlainText -Force
+$ServerCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_User, $ServerPasswd)
+
+Invoke-WebRequest "http://127.0.0.1:$($Config.Server_Port)/Cmd-Mine" -Credential $ServerCreds
+
diff --git a/Utils/API-Pause.ps1 b/Utils/API-Pause.ps1
new file mode 100644
index 0000000..522135c
--- /dev/null
+++ b/Utils/API-Pause.ps1
@@ -0,0 +1,14 @@
+
+Write-Host "Sending Pause command to NPlusMiner via API"
+
+. .\Includes\Include.ps1
+
+$Variables = [hashtable]::Synchronized(@{})
+$ConfigFile = ".\Config\Config.json"
+$Config = Load-Config -ConfigFile $ConfigFile
+
+$ServerPasswd = ConvertTo-SecureString $Config.Server_Password -AsPlainText -Force
+$ServerCreds = New-Object System.Management.Automation.PSCredential ($Config.Server_User, $ServerPasswd)
+
+Invoke-WebRequest "http://127.0.0.1:$($Config.Server_Port)/Cmd-Pause" -Credential $ServerCreds
+
diff --git a/Utils/WebUI.png b/Utils/WebUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..da67354f3a33693013298fff648653cc85f039c3
GIT binary patch
literal 89875
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
z