V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
myrual
V2EX  ›  推广

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

  •  
  •   myrual · 2019-02-18 10:23:22 +08:00 · 1742 次点击
    这是一个创建于 2113 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 条回复    2019-02-18 10:31:32 +08:00
    suktyo
        1
    suktyo  
       2019-02-18 10:27:01 +08:00
    好担心这玩意被注入一下


    那真是损失几个亿
    myrual
        2
    myrual  
    OP
       2019-02-18 10:31:32 +08:00
    可以来注入一下试试。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2713 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 09:04 · PVG 17:04 · LAX 01:04 · JFK 04:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.