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

第二课 一步一步教你用 Node.js 收发比特币

  •  
  •   myrual · 2019-01-23 19:21:50 +08:00 · 1773 次点击
    这是一个创建于 1924 天前的主题,其中的信息可能已经有所发展或是发生改变。

    上一篇教程中, 我们创建了自动回复消息的机器人。

    按本篇教程后学习后完成后,你的机器人将能够知道自己收到比特币,然后立即转比特币给用户。

    在开始之前, 开发者需要准备一些 config.js 的必备参数。

    用 mixin-cli 工具生成 AES key

    如果你看过 python,javascript 或 php 的代码,你会发现在这没有 Pin token 选项,这会有一个 AES key 来替代它。

    安装一个工具 mixin-cli,用它来生成 config.js

    cd mixin_net-nodejs-bot2
    yarn add mixin-cli
    

    下面将介绍一个工具来生成 AES key,还记得 Mixin.one 控制面板生成的数据吗? 从 mixin.one 的开发者中心,点击"Click to generate a new session",拷贝新生成的所有数据 Mixin.one Dashboard

    然后在终端里执行 mixin dapp:config dapp:config 是参数

    wenewzha:mixin_network-nodejs-bot wenewzhang$ ./node_modules/mixin-cli/bin/mixin dapp:config
    ? What is the DAPP session info Press <enter> to launch your preferred editor.
    

    如你所见,按"回车"将打开默认的编辑器,比如在我的电脑上,是打开 vim, 粘贴剪切版里的数据,然后保存并退出。 https://github.com/wenewzhang/mixin_network-nodejs-bot2/blob/master/paste-to-vim.png

    回车三次,就产生了一个临时文件,比如 config_mixin_1546851899846.js

    wenewzha:mixin_network-nodejs-bot wenewzhang$ ./node_modules/mixin-cli/bin/mixin dapp:config
    ? What is the DAPP session info Received
    ? Which config format do you prefer? [Plain Javascript] can be required from any js code
    ? What is the filename for generated config file config_mixin_1546851899846.js
    ✔︎ sessionInfo was successfully decoded!
    ✔︎ Config file written to /Users/wenewzhang/Documents/sl/mixin_network-nodejs-bot/config_mixin_1546851899846.js
    ✔︎ Press ctrl+v and then enter to view file content
    

    生成的 config_mixin_1546851899846.js 如下:

    // Generated with awesome https://github.com/wangshijun/mixin-cli
    module.exports = {
      clientId: '<PUT YOUR DAPP CLIENT_ID HERE>',
      clientSecret: '<PUT YOUR DAPP CLIENT_SECRET HERE>',
      assetPin: '762835',
      sessionId: '4ec58515-814f-421c-844d-8717696cf460',
      aesKey: 'jqZvcLnlqt73TsSWHLezKNjdIdWBZ/rqTQLJlT1BlRQ=',
      privateKey: `-----BEGIN RSA PRIVATE KEY-----
    MIICXAIBAAKBgQCJDrQG95rRXkGfli1KKNPCdYAbpE795p+A3q7PtgTYwUNal9uP
    UqeanDTmeMV5vSQu5f8n9M+60aytYcR1JetIQBMVGL4lVaAuAf1TkPT6GrbOvhdw
    pykk6Tdx454Ju7jwv+txuHKlrw+mrKG/pxCVQ6bAcwDkbae5mo8yeBRiQQIDAQAB
    AoGAALNZijuTyAQyU62B18IzqufM2tdRLA0UvaTlwdwNVEpQnNLv5WCnyKuJva/a
    Wo/z8mVsk3i14x+VQWGhjnO+KyNyS7H8S2HPp/FjTCEpPMgSFfQmHToNgNp0gTpu
    cHG5aUvUJYYVvUR3uGTlsZs006M1fNcc/7rAtBP8cwwYYn0CQQDVaZKju0VtRNuC
    85zVwfxRngGSxWNJLznTYEdrMlwkLLfkUakU5dA63s0Nh32vFb79GcYJ3BbQTH8u
    oXFfEmwnAkEApGhyMcV1myVA4vY2w1Mhd25e8rgqR0HSqdFLYPwz9mqVI9v/e5yc
    vxb5Pr+zJLxLKvHP6/D1iq9qzVcvfMj3VwJAFopiDJ0ZBiOBs+EbLZChn9U6gVAL
    3oz4ZJUEthPJm6CFg74ER8rGJZGmwskOw1FerMjuG9h9KF8MB9bRbKM7fQJBAKBM
    ggMLLubtRL3GKJD7jebfw03OyNIfWKJgwak3XgbF1tJW31wL0Dz0zmIjES0hNf0S
    NpMqpo3pCS5a8p8tZxMCQH744X2N3z3On2t0v559eXdJALuxIQKv1KbwQSv44T5Y
    REp2XzEpK6y/MfFSiCpc77fLlZ6lsOfufqwxwRn0Cvg=
    -----END RSA PRIVATE KEY-----`,
    };
    

    你还需要补充以下数据( clientID,clientSecret)才是一个完整的 config.js; 或者 只需要拷出 aesKey 替换你原来的 config.js.

    • clientId 在 dashboard 可以找到
    • clientSecret 左 dashboard 点击 "Click to generate a new secret"生成一个!
    • aesKey 我们新生成的数据 将它放入到正式的 config.js 里

    一个完整的 config.js 例子如下: here

    Hello world,币来了!

    我们再创建一个 app2.js 内容如下:

    app2.js

    const { SocketClient, isMessageType } = require('mixin-node-client');
    const { HttpClient } = require('mixin-node-client');
    const config = require('./config2');
    const client = new SocketClient(config);
    
    const ValidActions = ["ACKNOWLEDGE_MESSAGE_RECEIPT" ,"CREATE_MESSAGE", "LIST_PENDING_MESSAGES"];
    
    const receiverID = "0b4f49dc-8fb4-4539-9a89-fb3afc613747";
    const coinID = "6cfe566e-4aad-470b-8c9a-2fd35b49c68d";
    
    console.log('Supported MessageSenders by SocketClient', client.getMessageSenders());
    console.log(client.getMessageSenders());
    // Listen and react to socket messages
    client.on(
      'message',
      client.getMessageHandler(message => {
        console.log('Message Received', message);
        if (ValidActions.indexOf(message.action) > -1) {
          if (message.action === 'ACKNOWLEDGE_MESSAGE_RECEIPT') {console.log("ignore receipt");return;}
    
          if (isMessageType(message, 'text')) {
            console.log('----------------------text-------------------------');
            const text = message.data.data.toLowerCase();
            if ( (message.data.category === "PLAIN_TEXT") && (message.action === "CREATE_MESSAGE") ) {
              var parameter4IncomingMsg = {"message_id":message.data.message_id, "status":"READ"};
              var RspMsg = {"id":client.getUUID(), "action":"ACKNOWLEDGE_MESSAGE_RECEIPT", "params":parameter4IncomingMsg};
              client.sendRaw(RspMsg);
    
              if (text === 'pay') {
                let payLink = "https://mixin.one/pay?recipient=" +
                  config.clientId + "&asset=" +
                  "6cfe566e-4aad-470b-8c9a-2fd35b49c68d" +
                  "&amount=0.01" + '&trace=' + client.getUUID() +
                  "&memo=";
                return client.sendButton({
                    label: 'pay 0.01 EOS',
                    color: '#FF0000',
                    action: payLink,
                  },
                  message
                );
              }
    
        // todo: catch a error when balance insufficient
        //       (node:55535) UnhandledPromiseRejectionWarning: Error: Insufficient balance.
        // at HttpClient.(anonymous function) [as createTransfer] (/Users/wenewzhang/Documents/sl/mixin_network-nodejs-bot2/node_modules/mixin-node-client/lib/http.js:99:23)
        // at process.internalTickCallback (internal/process/next_tick.js:77:7)
              if (text === 'refund') {
                asyncRefundCall(coinID,'0.00001',receiverID);
              }
              return client.sendText( message.data.data, message);
            }
          }
          if (message.data && message.data.category === "SYSTEM_ACCOUNT_SNAPSHOT") {
              console.log("-----------the bot got money!---------------");
    
              var jsData = JSON.parse(Buffer.from(message.data.data, 'base64').toString('utf-8'));
              console.log(jsData);
    
    //let the server know that i have read this message
              var parameter4IncomingMsg = {"message_id":message.data.message_id, "status":"READ"};
              var RspMsg = {"id":client.getUUID(), "action":"ACKNOWLEDGE_MESSAGE_RECEIPT", "params":parameter4IncomingMsg};
              client.sendRaw(RspMsg);
    
              if (jsData.amount > 0) {
                //refund immediately
                asyncRefundCall(jsData.asset_id,jsData.amount,jsData.opponent_id);
              } else console.log("refund success!");
              console.log("-----------end of bot got money!---------------");
          }
          return Promise.resolve(message);
      } else console.log("unknow action")
      }));
    
    client.on('error', err => console.error(err.message));
    
    function refundInstant(_assetID,_amount,_opponent_id) {
      return new Promise(resolve => {
        var httpClient = new HttpClient(config);
        const Obj = {
          assetId: _assetID,
          recipientId: _opponent_id,
            traceId: httpClient.getUUID(),
            amount: _amount,
            memo: '',
        };
        console.log('resolve...');
        console.log(Obj);
        console.log("end of Obj");
        httpClient.transferFromBot(Obj);
      })
    }
    async function asyncRefundCall(_assetID,_amount,_opponent_id) {
      console.log('calling asyncCall');
      var result = await refundInstant(_assetID,_amount,_opponent_id);
      console.log(result);
    }
    
    

    源代码解释

    app2.js

    //定义可用的消息名称
    const ValidActions = ["ACKNOWLEDGE_MESSAGE_RECEIPT" ,"CREATE_MESSAGE", "LIST_PENDING_MESSAGES"];
    

    创建一个支付链接,响应用户的pay指令

    if (text === 'pay') {
      let payLink = "https://mixin.one/pay?recipient=" +
        config.clientId + "&asset=" +
        "6cfe566e-4aad-470b-8c9a-2fd35b49c68d" +
        "&amount=0.01" + '&trace=' + client.getUUID() +
        "&memo=";
      return client.sendButton({
          label: 'pay 0.01 EOS',
          color: '#FF0000',
          action: payLink,
        },
        message
      );
    }
    

    用户点击链接,依提示在 Mixin Messenger 里支付,支付后机器人会马上转回给用户,如下图: https://github.com/wenewzhang/mixin_network-nodejs-bot2/blob/master/pay-link.png 开发者可以支付任意的币给机器人,机器人在收到币后,立即转回给开发者! https://github.com/wenewzhang/mixin_network-nodejs-bot2/blob/master/transfer-any-tokens.jpg

    操作如下图所示: https://github.com/wenewzhang/mixin_network-nodejs-bot2/blob/master/pay-refund.jpeg

    源代码解释

    app2.js

    //定义可接受的消息
    const ValidActions = ["ACKNOWLEDGE_MESSAGE_RECEIPT" ,"CREATE_MESSAGE", "LIST_PENDING_MESSAGES"];
    

    用户发送 'pay' 指令后,机器人发回一个 payment 的链接

    if (text === 'pay') {
      let payLink = "https://mixin.one/pay?recipient=" +
        config.clientId + "&asset=" +
        "6cfe566e-4aad-470b-8c9a-2fd35b49c68d" +
        "&amount=0.01" + '&trace=' + client.getUUID() +
        "&memo=";
      return client.sendButton({
          label: 'pay 0.01 EOS',
          color: '#FF0000',
          action: payLink,
        },
        message
      );
    }
    
    if (message.data && message.data.category === "SYSTEM_ACCOUNT_SNAPSHOT") {
        var jsData = JSON.parse(Buffer.from(message.data.data, 'base64').toString('utf-8'));
    //let the server know that i have read this message
        var parameter4IncomingMsg = {"message_id":message.data.message_id, "status":"READ"};
        var RspMsg = {"id":client.getUUID(), "action":"ACKNOWLEDGE_MESSAGE_RECEIPT", "params":parameter4IncomingMsg};
        client.sendRaw(RspMsg);
        if (jsData.amount > 0) {
          //refund immediately
          asyncRefundCall(jsData.asset_id,jsData.amount,jsData.opponent_id);
        } else console.log("refund success!");
    }
    

    如果机器人收到币,jsData.amount 大于零;如果机器人支付币给用户,接收到的消息是一样的,唯一不同的是 jsData.amount 是一个负数.

    完整的例子请点击

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2295 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 02:50 · PVG 10:50 · LAX 19:50 · JFK 22:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.