如果想在 V2EX 获得更好的推广效果,欢迎了解 PRO 会员机制:
https://v2.zilu.eu.org/pro/about

如果你经常使用铜币置顶主题,持有 V2EX Solana Token 会在每日签到时获得额外铜币:
https://v2.zilu.eu.org/solana
myrual
V2EX  ›  推广

PHP 收发比特币教程第一课:创建机器人

  •  
  •   myrual · Feb 18, 2019 · 2638 views
    This topic created in 2665 days ago, the information mentioned may be changed or developed.

    Mixin Network 是一个免费的 极速的端对端加密数字货币交易系统. 在本章中,你可以按教程在 Mixin Messenger 中创建一个 bot 来接收用户消息, 学到如何给机器人转比特币 或者 让机器人给你转比特币. 英文原文

    创建一个接受消息的机器人

    通过本教程,你将学会如何用 PHP 创建一个机器人 APP,让它能接受消息.

    PHP 环境安装:

    本教程的程序基于 PHP 7 开发,所以你需要先安装 PHP7.2/PHP7.3 与 composer, 其中,composer 是 PHP 的包管理系统! on macOS

    brew update
    brew install [email protected]
    php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
    php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
    //将 PHP 安装到 /usr/local/opt/[email protected]/bin 目录,并取一个简单的各字:composer
    php composer-setup.php --install-dir=/usr/local/opt/[email protected]/bin --filename=composer
    php -r "unlink('composer-setup.php');"
    

    on Ubuntu

    apt update
    apt upgrade
    
    //install php 7.2
    apt-get install software-properties-common python-software-properties
    add-apt-repository -y ppa:ondrej/php
    apt-get update
    apt-get install php7.2 php7.2-cli php7.2-common
    //install composer
    php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
    php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
    //将 PHP 安装到 /usr/local/opt/[email protected]/bin 目录,并取一个简单的各字:composer
    php composer-setup.php --install-dir=/usr/local/bin --filename=composer
    php -r "unlink('composer-setup.php');"
    

    请确保 PHP 与 composer 安装在$PATH 包含的目录之内,直接运行php -vcomposer -V 就可以检查出来,如果提示如下,表示安装正确!

    wenewzha:minecraft wenewzhang$ php -v
    PHP 7.2.13 (cli) (built: Dec  7 2018 10:41:23) ( NTS )
    Copyright (c) 1997-2018 The PHP Group
    Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
        with Zend OPcache v7.2.13, Copyright (c) 1999-2018, by Zend Technologies
    wenewzha:minecraft wenewzhang$ composer -V
    Composer version 1.8.0 2018-12-03 10:31:16
    

    如果提示 command not found,表示 bash 没有在$PATH 下找到 php,请重复以上的安装步骤!

    wenewzha:mixin_network-nodejs-bot2 wenewzhang$ php -v
    -bash: php: command not found
    

    创建你的项目

    到你的工作文档中,创建一个目录,并取一个名字,比如:mixin_labs-php-bot

    mkdir mixin_labs-php-bot
    cd mixin_labs-php-bot
    

    转到新创建的项目目录下, 执行 composer init, 依提示完成 composer.json 的创建,

    root@iZj6cbmqen2lqp7l48nfgkZ:~/mixin_labs-php-bot# composer init
     Welcome to the Composer config generator
    This command will guide you through creating your composer.json config.
    Package name (<vendor>/<name>) [user/mixin_labs-php-bot]:
    Description []: PHP 7 bot for Mixin Messenger
    Author [, n to skip]: JimmyZhang <[email protected]>
    Minimum Stability []:
    Package Type (e.g. library, project, metapackage, composer-plugin) []:
    License []:
    Define your dependencies.
    Would you like to define your dependencies (require) interactively [yes]? no
    Would you like to define your dev dependencies (require-dev) interactively [yes]? no
    {
       "name": "user/mixin_labs-php-bot",
       "description": "PHP 7 bot for Mixin Messenger",
       "authors": [
           {
               "name": "JimmyZhang",
               "email": "[email protected]"
           }
       ],
       "require": {}
    }
    Do you confirm generation [yes]? yes
    

    本教程引用了 mixin-sdk-phpRatchet pawl, mixin-sdk-php 是一个 Mixin Network PHP SDK , Ratchet pawl 是一个 WebSocket 客户端. 打开 composer.json, 在"require"增加两行引用:

    "require": {
        "exinone/mixin-sdk-php": "^1.1",
        "ratchet/pawl": "^0.3.3",
    },
    

    保存 composer.json 后,执行 composer install 来下载:

    composer install
    

    下载完成后,目录下会出现一个 vendor 的子目录。

    root@iZj6cbmqen2lqp7l48nfgkZ:~/mixin_labs-php-bot# ls
    composer.json  composer.lock  vendor
    

    如果你是 git 克隆的源代码,直接执行 composer install 来下载依赖包。

    创建第一个机器人 APP

    按下面的提示,到 mixin.one 创建一个 APP

    生成相应的参数

    记下这些生成的参数 它们将用于 config.php 中.

    在项目目录下,创建 config.php,将生成的参数,替换成你的!

    config.php

    return [
        'mixin_id'      => '7000101716',
        'client_id'     => 'a1ce2967-a534-417d-bf12-c86571e4eefa',
        'client_secret' => '7339866727d24eeec1c4ebb6c634fd25a7b9057ee6d5939cca9b6b9fc15f4d1f',
        'pin'           => '512772',
        'pin_token'     => 'abRdNq6soRALRG434IgR7WS/qP7LOcpfviqSfWfABdIKyZGLnWXFMrVCHpChIkBRGRAcsUguni0OoNsShddPVL3qoD5fxbF5dRUiRv14urH1Pmdl6zIZdCH159QMr5wLmmSHSGu2AihNkUHUo3bAJsrvOW0nke5y6R5YE/pNNfo=',
        'session_id'    => '51faabbf-48ff-4df2-898d-e9b318afae35',
        'private_key'   => <<<EOF
    -----BEGIN RSA PRIVATE KEY-----
    MIICXQIBAAKBgQCuKI65sJR9lQ1+kyKouWu3CpmkPdJKaFqKVMEWk9RRH1Wgju9n
    z/y5MiBVZKUeeIYtwrCNKbbdkSPqMoj1kLh5XUk4HaV9DUt+s9USBHOgU8m5Pxov
    Km+HQ+Pam62lHWn6ClYaNrDihpcdDg9i7Y8hY1cgKiUcdkFQmDQ9lz2VHwIDAQAB
    AoGANHJSSOk8TnVMkwmMLnNoVL8EdcmIQpAac/4CB+KM1cEx8CAbSJAB82N9CTo9
    32c8QRuYP2qIf0DuJ+EADbN/Wc3o9zRY3dkbnLo144g3YaKwDccSgUMux03ANHlP
    MEPDxOUbxJTRPXmKgUZmGJrkAClGbr3pPyQDDHDWRQc9JUECQQDT7pUYcXtu+hSc
    nAlZllzqkBG2gZrDYpPJ0JirpfNhaApBo+CGZYKQ1961o6+HcI9gZmZA8hPEhT6p
    PlubjqxbAkEA0l89du8TIUGrY9/sxyfZif6aeEztXPwBHZ9r8dm0L8Mlu5zTrOX2
    SUgu3znM6djmuRMS45iPHJbPkvw9ilaljQJBAJRN323Ec/D79ZKGKpDThN/rw0lo
    tolFoU/Xtg5fycl/CbZXXFYQEOcU+Nc43Ss1HFAEOEf4Xtbluyyp42ce1wMCQElv
    P4htyhK41rglaYTXr0NRYeCOkej8evM5PDgPU6u8hkZoZyeamo9YKCx6A8K5mUiP
    lO9nyMUlC852SJEqz90CQQDBguGg5GGcfehpIZwERlMJgKGg1+13/9GfnEPdAW2v
    px7DZoMG/pQ/SEa53tJHmGGD9+qyp93z/fEPXsD5RSwx
    -----END RSA PRIVATE KEY-----
    EOF
        ,  //import your private_key
    ];
    

    需要替换的参数包括:mixin_id, client_id, client_secret, and the pin, pin token, session_id, private key.

    经典的 Hello world

    在项目目录下创建一个 app.php 文件,将下面的代码拷进去:

    <?php
    
    require __DIR__ . '/vendor/autoload.php';
    use ExinOne\MixinSDK\Traits\MixinSDKTrait;
    use ExinOne\MixinSDK\MixinSDK;
    use Ramsey\Uuid\Uuid;
    use Ratchet\RFC6455\Messaging\Frame;
    
    $loop = \React\EventLoop\Factory::create();
    $reactConnector = new \React\Socket\Connector($loop, [
        'timeout' => 15
    ]);
    $connector = new \Ratchet\Client\Connector($loop,$reactConnector);
    class callTraitClass {
      use MixinSDKTrait;
      public $config;
      public function __construct()
      {
          $config = require(__DIR__.'/config.php');
          $this->config        = $config;
      }
    }
    $callTrait = new callTraitClass();
    $Token = $callTrait->getToken('GET', '/', '');
    // $connector('ws://127.0.0.1:9000', ['protocol' => 'Mixin-Blaze-1'], ['Origin' => 'http://localhost',
    $connector('wss://blaze.mixin.one', ['protocol' => 'Mixin-Blaze-1'],[
                                        'Authorization' => 'Bearer '.$Token
                                          ])
    ->then(function(Ratchet\Client\WebSocket $conn) {
        $conn->on('message', function(\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($conn) {
            $jsMsg = json_decode(gzdecode($msg));
            print_r($jsMsg);
            if ($jsMsg->action === 'CREATE_MESSAGE' and property_exists($jsMsg,'data')) {
              echo "\nNeed reply server a receipt!\n";
              $RspMsg = generateReceipt($jsMsg->data->message_id);
              $msg = new Frame(gzencode(json_encode($RspMsg)),true,Frame::OP_BINARY);
              $conn->send($msg);
    
              if ($jsMsg->data->category === 'PLAIN_TEXT') {
                    $msgData = sendPlainText($jsMsg->data->conversation_id,
                                              base64_decode($jsMsg->data->data));
                    $msg = new Frame(gzencode(json_encode($msgData)),true,Frame::OP_BINARY);
                    $conn->send($msg);
              } //end of PLAIN_TEXT
            } //end of CREATE_MESSAGE
    
        });
        $conn->on('close', function($code = null, $reason = null) {
            echo "Connection closed ({$code} - {$reason})\n";
        });
    /*                   start listen for the incoming message          */
        $message = [
            'id'     => Uuid::uuid4()->toString(),
            'action' => 'LIST_PENDING_MESSAGES',
        ];
        print_r(json_encode($message));
        $msg = new Frame(gzencode(json_encode($message)),true,Frame::OP_BINARY);
        $conn->send($msg);
        // $conn->send(gzencode($msg,1,FORCE_DEFLATE));
    }, function(\Exception $e) use ($loop) {
        echo "Could not connect: {$e->getMessage()}\n";
        $loop->stop();
    });
    
    $loop->run();
    
    
    function sendPlainText($conversation_id,$msgContent):Array {
    
       $msgParams = [
         'conversation_id' => $conversation_id,
         'category'        => 'PLAIN_TEXT',
         'status'          => 'SENT',
         'message_id'      => Uuid::uuid4()->toString(),
         'data'            => base64_encode($msgContent),//base64_encode("hello!"),
       ];
       $msgPayButton = [
         'id'     =>  Uuid::uuid4()->toString(),
         'action' =>  'CREATE_MESSAGE',
         'params' =>   $msgParams,
       ];
       return $msgPayButton;
    }
    
    function generateReceipt($msgID):Array {
      $IncomingMsg = ["message_id" => $msgID, "status" => "READ"];
      $RspMsg = ["id" => Uuid::uuid4()->toString(), "action" => "ACKNOWLEDGE_MESSAGE_RECEIPT",
                  "params" => $IncomingMsg];
      return $RspMsg;
    }
    
    

    保存,并在终端里执行 app.php

    php app.php
    

    如果一切正常,提示如下:

    wenewzha:mixin_labs-php-bot wenewzhang$ php app.php
    a1ce2967-a534-417d-bf12-c86571e4eefa{"id":"4454b6c5-4a89-440c-bd22-7a79cf4954ca","action":"LIST_PENDING_MESSAGES"}stdClass Object
    (
        [id] => 4454b6c5-4a89-440c-bd22-7a79cf4954ca
        [action] => LIST_PENDING_MESSAGES
    )
    

    在手机安装 Mixin Messenger,增加机器人为好友,(比如这个机器人是 7000101639) 然后发送消息给它,效果如下!

    源代码解释

    WebSocket 是建立在 TCP 基础之上的全双工通讯方式,我们需要建立一个 loop 循环来维持通迅。

    $loop = \React\EventLoop\Factory::create();
    $reactConnector = new \React\Socket\Connector($loop, [
        'timeout' => 15
    ]);
    $connector = new \Ratchet\Client\Connector($loop,$reactConnector);
    

    机器人 APP 通过 Mixin Messenger 服务器来接收用户发过来的消息,连接服务器,需要 Token 来验证用户的身份, 详细资料可参考如下链接: Token 认证, 接收服务器消息

    mixin-sdk-php 实现了令牌 Token 的生成(getToken), 调用代码如下:

    class callTraitClass {
      use MixinSDKTrait;
      public $config;
      public function __construct()
      {
          $config = require(__DIR__.'/config.php');
          $this->config        = $config;
      }
    }
    $callTrait = new callTraitClass();
    $Token = $callTrait->getToken('GET', '/', '');
    

    连接到服务器,注意协议类型与 Token:

    $connector('wss://blaze.mixin.one', ['protocol' => 'Mixin-Blaze-1'],[
                                        'Authorization' => 'Bearer '.$Token
                                          ])
    

    向服务器发送"LIST_PENDING_MESSAGES",这样服务器才会将收到的消息发送给机器人 APP.

    /*                   start listen for the incoming message          */
        $message = [
            'id'     => Uuid::uuid4()->toString(),
            'action' => 'LIST_PENDING_MESSAGES',
        ];
        print_r(json_encode($message));
        $msg = new Frame(gzencode(json_encode($message)),true,Frame::OP_BINARY);
        $conn->send($msg);
    

    增加侦听 onMessage 接收并分析消息:

    ->then(function(Ratchet\Client\WebSocket $conn) {
        $conn->on('message', function(\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($conn) {
            $jsMsg = json_decode(gzdecode($msg));
            print_r($jsMsg);
            if ($jsMsg->action === 'CREATE_MESSAGE' and property_exists($jsMsg,'data')) {
              echo "\nNeed reply server a receipt!\n";
              $RspMsg = generateReceipt($jsMsg->data->message_id);
              $msg = new Frame(gzencode(json_encode($RspMsg)),true,Frame::OP_BINARY);
              $conn->send($msg);
    
              if ($jsMsg->data->category === 'PLAIN_TEXT') {
                    $msgData = sendPlainText($jsMsg->data->conversation_id,
                                              base64_decode($jsMsg->data->data));
                    $msg = new Frame(gzencode(json_encode($msgData)),true,Frame::OP_BINARY);
                    $conn->send($msg);
              } //end of PLAIN_TEXT
            } //end of CREATE_MESSAGE
    
        });
        $conn->on('close', function($code = null, $reason = null) {
            echo "Connection closed ({$code} - {$reason})\n";
        });                                      
    

    Mixin Messenger 支持的消息类型很多,具体可到下面链接查看: WebSocket 消息类型.

    每接收到一个消息,需要按消息编号(message_id)给服务器回复一个"已读"的消息,避免服务器在机器人重新登入后,再次发送处理过的消息!

    echo "\nNeed reply server a receipt!\n";
    $RspMsg = generateReceipt($jsMsg->data->message_id);
    $msg = new Frame(gzencode(json_encode($RspMsg)),true,Frame::OP_BINARY);
    $conn->send($msg);
    
    function generateReceipt($msgID):Array {
      $IncomingMsg = ["message_id" => $msgID, "status" => "READ"];
      $RspMsg = ["id" => Uuid::uuid4()->toString(), "action" => "ACKNOWLEDGE_MESSAGE_RECEIPT",
                  "params" => $IncomingMsg];
      return $RspMsg;
    }
    

    完成

    现在你的机器人 APP 运行起来了,你打算如何改造你的机器人呢?

    完整的代码在这儿

    2 replies    2019-02-18 10:31:32 +08:00
    suktyo
        1
    suktyo  
       Feb 18, 2019
    好担心这玩意被注入一下


    那真是损失几个亿
    myrual
        2
    myrual  
    OP
       Feb 18, 2019
    可以来注入一下试试。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   888 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 40ms · UTC 22:17 · PVG 06:17 · LAX 15:17 · JFK 18:17
    ♥ Do have faith in what you're doing.