我如何使用纯PowerShell创建Minecraft付款接受系统


在本文中,我们将使用Powershell将无用的甜甜圈拧到Minecraft香草服务器上。该方法的优点是,Minecraft只是使用控制台命令实施自动付款的一种特殊情况。我们只是听取付款系统向我们发送的内容,然后将其打包成一个团队。最重要的是-没有插件。
并且我们将接受通过PayPal付款。最重要的是,为了开始接受付款,您不需要更改代码,PayPal会向我们发送您所需的一切。我们将使用网站上的按钮,以便网站可以使用纯HTML。我们从支付系统本身的复杂性中抽象出来,仅关注代码中的要点。

顺便说一句,如果您仔细阅读所有模块并在其中指出或纠正儿童的错误,作者将非常高兴。这是github项目链接

关于IPN的几句话


IPN


我们将通过按钮接受付款。按钮不需要您的任何后端,它们使用纯HTML格式,并且还具有自己的字段。

按钮触发IPN-即时付款通知,其中数据被发送到我们的WebListener。我们将在下面考虑IPN结构。

此外,拥有PayPal帐户的任何人都可以创建自己的按钮。
IPN没有完整的PayPal REST API,但是可以在其上实现基本功能。实际上,我们正在考虑的IPN并不是完全意义上的REST API,仅仅是因为PayPal本身不希望我们得到任何东西,除了代码200。

提升WebListener


出于安全原因,PayPal不会通过HTTP发送请求,因此我们需要颁发证书才能开始使用。 

作者使用WinAcme您可以将证书颁发给任何域,并且需要将证书放入本地证书存储中。顺便说一下,WinAcme位于映像中磁盘的根目录中。

#     
Get-ChildItem -Path Cert:\LocalMachine\My 
 
#      443 .
netsh http add sslcert ipport=0.0.0.0:443 certhash=D106F5676534794B6767D1FB75B58D5E33906710 "appid={00112233-4455-6677-8899-AABBCCDDEEFF}"

Powershell可以使用.net中的类,这使其几乎等于.net。首先,使用HttpListener类提高Web服务器。

#   .net
$http = [System.Net.HttpListener]::new() 
 
#   
$http.Prefixes.Add("http://donate.to/")
$http.Prefixes.Add("https://donate.to/")
 
# 
$http.Start()

要验证一切正常,请运行netstat。



如果我们的脚本开始侦听列表中的端口443,则表明您正确完成了所有操作,我们可以继续接受请求处理。只是不要忘记防火墙。

接受请求


使用IPN Simulator,我们可以向自己发送一个测试POST请求,以查看它是什么。但是您不能在其中包含自己的字段,因此作者建议您制作一个按钮并立即从您自己的手中购买东西。IPN历史记录将通过您将使用的按钮显示一个正常请求。作者只是通过购买一卢布的煤来做到这一点。

我们将接受使用While循环。当Web服务器运行时,我们可以读取传入的数据流。

while ($http.IsListening) {
 
  $context = $http.GetContext()
 
  if ($context.Request.HttpMethod -eq 'POST' -and $context.Request.RawUrl -eq '/') {
 
    #  POST 
    $Reader = [System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd()
    
    #  .
    $DecodedContent = [System.Web.HttpUtility]::UrlDecode($Reader)
 
    #   .
    $Payment | Format-Table
 
    #  200 OK   .
    $context.Response.Headers.Add("Content-Type", "text/plain")
    $context.Response.StatusCode = 200
    $ResponseBuffer = [System.Text.Encoding]::UTF8.GetBytes("")
    $context.Response.ContentLength64 = $ResponseBuffer.Length
    $context.Response.OutputStream.Write($ResponseBuffer, 0, $ResponseBuffer.Length)
    $context.Response.Close()
  }
}

如果您得到这样的粉丝,请申请:

$Payment = $DecodedContent -split "&" | ConvertFrom-StringData



之后,您最终将收到一个普通对象,其中所有Value为String。如果您不想深入研究代码,而只想接受来自某人API的请求,则



可以在此处退出阅读

这是开箱即用,复制和使用的代码:

# 
$http = [System.Net.HttpListener]::new() 
 
# ,   
$http.Prefixes.Add("http://localhost/")
$http.Prefixes.Add("https://localhost/")
 
$http.Start()
 
while ($http.IsListening) {
 
  $context = $http.GetContext()
 
  if ($context.Request.HttpMethod -eq 'POST' -and $context.Request.RawUrl -eq '/') {
 
    #  POST 
    $Reader = [System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd()
    
    #  .
    $DecodedContent = [System.Web.HttpUtility]::UrlDecode($Reader)
          
    #  IPN   
    $Payment = $DecodedContent -split "&" | ConvertFrom-StringData
 
    #   .
    $Payment | Format-Table
 
    #  200 OK   .
    $context.Response.Headers.Add("Content-Type", "text/plain")
    $context.Response.StatusCode = 200
    $ResponseBuffer = [System.Text.Encoding]::UTF8.GetBytes("")
    $context.Response.ContentLength64 = $ResponseBuffer.Length
    $context.Response.OutputStream.Write($ResponseBuffer, 0, $ResponseBuffer.Length)
    $context.Response.Close()
  }
}

我的世界的细微差别


因此,我们弄清楚了如何接收有关付款的警报,现在我们可以将其记入贷方。但是,这里也不是那么简单。问题在于游戏不会提供物品或更改不在服务器上的玩家的状态。也就是说,我们需要等到一个人进入服务器后再给他付款。

因此,请注意吸烟者的一般概念,以记帐付款。



通过上面的侦听器接收付款;仅向其中添加了一行以将该对象写入文件。 Complete-Payment(处理器)查看昵称,并将其与文件名匹配。如果找到文件,则为rcon编译命令并执行它。

起始矿作者在上一篇文章中所写的内容略有修改。现在,他聆听结论,查看玩家的昵称,并将其传递给支付处理器。

进行真实的回调


不使用插件,我们将进行真正的回调。为此,对Start-Minecraft进行了修改。现在,他不仅知道如何将StdOut添加到文件中,而且还按照常规时间表遍历每一行。幸运的是,当玩家进入服务器时,Minecraft会留下非常具体的消息。

[04:20:00 INFO]: UUID of player XXPROHUNTERXX is 23e93d2e-r34d-7h15 -5h17-a9192cd70b48

从此行中选择昵称非常容易。这是我们从Stdout字符串获取数据所需的所有代码。

$Regex = [Regex]::new("of player ([^ ]+)")
 
powershell.exe -file ".\Start-MinecraftHandler.ps1" -type $type -MinecraftPath $MinecraftPath | Tee-Object $LogFile -Append | ForEach-Object {
 
     Write-host $_
        
    $Player = $Regex.Matches($_).value -replace "of player "
        
    if ($true -eq $Regex.Matches($_).Success) {
        #   
    }
}


将新行输入到$ _管道中,我们将其写在控制台窗口中并定期进行检查。常规本身会在工作时通知我们,这非常方便。

从这里我们可以调用任何代码。例如,使用相同的RCON,我们可以在PM中向玩家打招呼,在不和谐中使用Bot通知有人已登录到服务器,禁止将死,等等。

付款


自从我们开始处理付款以来,我们希望至少拥有关于操作和执行的操作历史的相当完整的数据,因为可以这么说,因为我们正在谈论带有两个零的数字。

作者想让一切都变得非常简单,而不是模拟基础。让我们看一下NoSQL方法。让我们创建自己的类,该类会将所有接受的付款导入Json文件中的/ payment /文件夹。

    class Payment {
        #  .
        [datetime]$Date = [datetime]::ParseExact($i.payment_date, "HH:mm:ss MMM dd, yyyy PDT", [System.Globalization.CultureInfo]::InvariantCulture)
        # 
        [string]$Item = $i.item_name
        # 
        [UInt16]$Quantity = $i.Quantity
        #    
        [UInt16]$AmountPaid = $AmountPaid -as [UInt16]
        #     
        [string]$Currency = $i.mc_currency
        # ,   
        [string]$Player = $i.option_selection1
    
        [bool]$Completed = $false
        [UInt16]$ItemId = $i.item_number
    }
/source>

    , , ,          .

 ,    <b>option_selection1</b> –   .      input,   ,     .
     <b>option_selection1</b>,<b>option_selection2</b>   .

      ,     ,      .

<source lang="powershell"> #     Payment,        .
    $Payment = [Payment]::new()
    $Payment | Format-Table
    #  ,   ----
    $FileName = $Payment.Player + "-" + $Payment.date.Hour + "-" + $Payment.date.Minute + "-" + $Payment.date.Day + "-" + $Payment.date.Month + "-" + $Payment.date.Year + ".json"
 
# ,      
    $JsonPath = Join-Path $MinecraftPath \payments\Pending $FileName
    
    #   
    $Payment | ConvertTo-Json | Out-File $JsonPath

这就是我们的听众所需要的。从PayPal接收数据并写入文件。

我们进行付款处理


该处理程序将称为较早编写的常规处理程序。我们将玩家的昵称转移到模块上,就是这样。接下来,启动一个新脚本,该脚本搜索文件,如果有文件,它将为播放器提供写入文件中的项目。

powershell.exe -file "C:\mc.fern\Start-MinecraftHandler.ps1" -type $type -MinecraftPath $MinecraftPath | Tee-Object $LogFile -Append | ForEach-Object {
       
        #     ,      .
        Write-host $_   
 
        # Regex     
        if ($true -eq $Regex.Matches($_).Success) {
            
            #       
            $Player = $Regex.Matches($_).value -replace "of player "
            
            #  ,       
            Complete-Payment -Player $Player
        }
    }

触发常规性后,将启动一个模块来完成付款,即,为玩家提供一个物品。为此,该脚本在/ Payments / Pending /文件夹中,搜索包含进入游戏的玩家昵称并读取其内容的文件。

现在,您需要收集服务器的命令并将其发送到那里。它将从文件中收集。我们知道记录了玩家的昵称,物品的名称及其ID,还记录了多少件,剩下的只是向游戏服务器发送命令。为此,我们将使用mcrcon

#    
    $JsonPath = Join-Path $MinecraftPath\payments\Pending -ChildPath $Player*
    $i = $JsonPath | Get-Item | Where-Object { !$_.PSIsContainer } | Get-Content | ConvertFrom-Json -ErrorVariable Errored
 
    #      
    if ($null -ne $i) {
 
        #  
        $Command = '"' + "give " + $i.Player + " " + $i.Item + " " + $i.Quantity + '"'
        Write-host $Command -ForegroundColor Green
    
        #   
        Start-Process -FilePath mcrcon.exe -ArgumentList "-H localhost -p 123 -w 5 $Command"
    
        # ,      
        $JsonPath = Join-Path $MinecraftPath\payments\Pending -ChildPath $FileName
        
        #   
        $i | ConvertTo-Json | Out-File $JsonPath
    
        #     
        Move-Item  -Path $JsonPath -Destination $MinecraftPath\payments\Completed
    }

我们在一个方便的模块中完成所有任务


Java进程和WebListener进程需要不同的线程,但是作者对分别运行WebListener和分别运行服务器的需求不满意。作者希望一个团队一次完成所有任务。

因此,使用Powershell 7,我们将同时启动该程序。它将帮助我们:

ForEach-Object -Parallel {}

该cmdlet与inputObject一起使用,因此我们将其输入一个简单的数组,并使用开关共享流。

"A", "B" | ForEach-Object -Parallel {
 
    Import-Module ".\Start-Minecraft.ps1"
 
    Import-Module ".\Start-WebListener.ps1"
 
    switch ($_) {
        "A" {
            Start-WebListener -Path "C:\mc\"
        }
        "B" {
            Start-Minecraft -Type Vanilla -LogFile ".\stdout.txt" -MinecraftPath "C:\mc\"
        }
        
    }
}

因此,以拐杖的方式,我们从一个终端启动了两个不同的过程,甚至没有丢失任何输入。但是还有另一个问题。WebListener会在常规服务器关闭后锁定控制台,并且不希望移至任何位置。

为了不每次都重新启动终端,向Start-MinecraftHandler.ps1和Start-WebListener.ps1添加了一个随机密钥,这将通过WebListener上的POST停止服务器。

Start-MinecraftHandler.ps1在记录成功完成时执行以下命令:

Invoke-WebRequest -Method Post -Uri localhost -Body $StopToken | Out-Null

$ StopToken包含一个随机数字值,启动脚本会将其随机传递给侦听器和处理程序。侦听器查看它在请求中收到的内容,如果请求正文与$ StopToken相匹配,则将其关闭。

if ($DecodedContent -eq $StopToken) {
        Write-Host "Stopping WebListener"
        $context.Response.Headers.Add("Content-Type", "text/plain")
        $context.Response.StatusCode = 200
        $ResponseBuffer = [System.Text.Encoding]::UTF8.GetBytes("")
        $context.Response.ContentLength64 = $ResponseBuffer.Length
        $context.Response.OutputStream.Write($ResponseBuffer, 0, $ResponseBuffer.Length)
        $context.Response.Close()
        $http.Close()
        break
      }

足够安全,只有RAM知道令牌,没有其他人知道。所有模块均从PowerShell 7下启动,并且PowerShell 7的模块路径与Windows Powershell中的路径不同。一切都堆放在这里。自己编写时请记住。

C:\Program Files\PowerShell\7\Modules

我们制作一个配置文件


为了使所有这些耻辱都可以使用而不会感到头疼,您需要制作一个正常的配置文件。该文件将包含变量,仅此而已。该配置使用标准:

Import-Module $MinecraftPath\config.ps1 -Force

我们需要指出最重要的事情。被窃听的域是常规域,用于寻找玩家的昵称,因为输出的版本可能不同,而密码则来自rcon。

看起来像这样:

#,    
$DomainName = "localhost"
 
# ,      
# ,  
$RegExp = "of player ([^ ]+)"
#    ,   ,  .
$RegExpCut = "of player "
 
#  rcon,     server.properties
$rconPassword = "123"

最好将配置放在服务器文件夹中,因为脚本正在root-MinecraftPath中寻找它。

如何使用所有这些?


首先,这些脚本已安装并可以在Ruvds 市场中使用,但是,如果您还没有客户端或尚未尝试映像,则这里是存储库中所有文件的链接,请随时提交。 

  1. 下载并安装PowerShell 7
  2. 下载并解压缩模块档案


现在,所有必要的模块和命令已经出现。他们在做什么?

起始矿


选项:

-键入
Forge或Vanilla。它可以从Server.Jar或Forge中启动服务器,并选择文件夹中的最新版本。

-MinecraftPath
指向将从中启动服务器的文件夹。

-LogFile
收集日志的另一种方法。表示将写入控制台中出现的所有内容的文件。

-StartPaymentListener
与服务器一起启动并接受付款。付款接受本身可以作为单独的模块使用。替换Start-Weblistener cmdlet

启动网站监听器


启动付款接受模块。

-MinecraftPath
指向带有配置文件的文件夹。

-StopToken
指定-Body HTTP POST请求以停止WebListener'a。

结论:


好吧,奇迹确实发生了。


All Articles