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

第一课 一步一步教你用 nodejs 创建一个 Mixin Network 机器人

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

    第一课 一步一步教你用 nodejs 创建一个 Mixin Network 机器人

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

    课程简介

    1. 创建一个接受消息的机器人
    2. 机器人:接受加密货币并立即退还用户

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

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

    Node.js 的环境安装

    本教程是用 node.js 写的,在开始之前,我们先安装 node 与 yarn

    macOS

    brew install node yarn
    

    Ubuntu

    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
    apt update
    apt upgrade
    apt install node yarn
    

    下面我们来创建一个项目目录

    打开终端,切换到你的工作目录,创建一个 nodejs-bot 的目录

    mkdir nodejs-bot
    cd nodejs-bot/
    

    运行 yarn init 指令,按提示完成项目的创建, 完成后会生成 package.json 文件,代码例子如下:

    yarn init
    
    {
      "name": "nodejs-bot",
      "version": "1.0.0",
      "main": "app.js",//默认生成的名字是 main.js
      "license": "MIT"
    }
    

    本教程需要依赖一个 SDK, wangshijun/mixin-node-client. 所以我们先下载这个库. 在新生成的项目目录下,执行 yarn add mixin-node-client 来添加 mixin-node-client

    yarn add mixin-node-client
    

    现在,package.json 会增加下面几行:

    "dependencies": {
      "mixin-node-client": "^0.6.0"
    }
    

    如果你是克隆这个教程,在项目目录下执行 yarn 来下载安装依赖的软件包.

    创建你的第一个机器人

    在写代码之前,我们先看一下面的图文教程,创建一个机器人 APP 教程.

    记住下面三项,这是机器人发送接收消息所必须的: user id, session id, private key, Mixin Network 使用这三项进行数字签名。

    | 关键字 | 描述 | 例子 | | --- | -------------------------------------------- | ------------------------------------------------- | user id | 机器人的唯一标识, uuid | 21042518-85c7-4903-bb19-f311813d1f51 | | session id | 会话标识, uuid | 5eb96d87-028e-4199-a6d3-6fc7da8dfe41 | | private key | RSA 私钥 | -----BEGIN RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----

    mixin_network-keys

    创建一个 config.js 文件, 替换 clientID 为你的机器人的 id, sessionId 为你的机器人的 session id, privateKey 为你的私钥,aesKey,clientSecret, assetPin 我们后面才需要,这里可先不修改,但请保留这个数据不要删除!

    config.js

    // NOTE: please update this config file with your own
    module.exports = {
      clientId: '21042518-85c7-4903-bb19-f311813d1f51',
      clientSecret: 'will-generate-later',
      assetPin: 'will-generate-later',
      sessionId: '6ca194a4-727f-4e5f-a348-3c62987536ba',
      aesKey: 'will-generate-later',
      privateKey: `-----BEGIN RSA PRIVATE KEY-----
    MIICWwIBAAKBgQC1P7QK7rK8lyX7X5t4A/reu7Q94xJkAllf1NPsW7zUdVgs/BCV
    f4RA6YK2prTpHHqXSCAzToEmou8R0xBMdnT/IQqijt0NzMpvrphiQrKrXEHrKrLm
    at6eZHYvLoGEPYjVq6RrgLkt2Mjld+RfHWd4zHXeqSCVfHAH67+gcPHYCwIDAQAB
    AoGADGotoeYRthtATcSRuJnFMEZ5JRgNpW4HwymnznPGLmNPQ92MIUFXxL555prq
    n2EFAKG/GuSQsh3M9FKZtjMS9l0aXpXy4T4ieBptkhahKbGVMLbQBru8wo/Pow3r
    r+DuNJs64ELvBYyydS7r1Fm/mtrd38Aq+4+04Z3UDW50AUkCQQDuhy8FoA3TKdZM
    mIEiPFb2dW8ohe8MsGM370S8HFPk7kdCaarJbiJCx1E+KjUxbkAeEFcLqKgaALTu
    IVCikIHNAkEAwoZvPaY0yFB1+V8HuToIR4X7AqWMy6WTBZ9U4wp34aNO21DLcrqf
    P40FHrHvqbWNK5bS8nSxLiv0kYL6+ezJNwJAO/GxOYKttsGu33T8DvSHDk0Y8GAo
    YVH6vVXeOkAMPV48fk47439QEOQyYKMO1ytT5bpJhd6O0GoZDjdFInWaiQJAAq4l
    hDzxBz2MkpYLnjK9gHbJIZ00Vm3+m5o5ajNvuW0tnfn8A6WsogyIYIblHXqB6nLW
    jz6qXk9+vC6I1L69ewJAasE+oC3TMblSOC9xqeBQgm8BPhb0UwJL4UuZLOSyUETr
    +bAwyiZ37Cc7r/nxKhVH+FwMCVoeNUMcRIYYMRjwmg==
    -----END RSA PRIVATE KEY-----`,
    };
    
    

    Hello world

    在项目目录下创建一个 app.js, 并将下面的代码粘贴进去.

    const { SocketClient, isMessageType } = require('mixin-node-client');
    const { HttpClient } = require('mixin-node-client');
    const config = require('./config');
    const client = new SocketClient(config);
    const ValidActions = ["ACKNOWLEDGE_MESSAGE_RECEIPT" ,"CREATE_MESSAGE", "LIST_PENDING_MESSAGES"];
    
    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')) {
            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') {
              // todo: pay
              }
              return client.sendText(text, message);
            }
          }
    
          return Promise.resolve(message);
      } else console.log("unknow action")
      }));
    client.on('error', err => console.error(err.message));
    

    开始执行

    node app.js
    

    如果你的配置文件有错,可能会出现下面的提示:

    ➜  nodejsdemo node app.js
    Supported MessageSenders by SocketClient [ 'sendText',
      'sendImage',
      'sendVideo',
      'sendData',
      'sendSticker',
      'sendContact',
      'sendButton',
      'sendButtons',
      'sendApp' ]
    [ 'sendText',
      'sendImage',
      'sendVideo',
      'sendData',
      'sendSticker',
      'sendContact',
      'sendButton',
      'sendButtons',
      'sendApp' ]
    Message Received { id: '00000000-0000-0000-0000-000000000000',
      action: 'ERROR',
      error:
       { status: 202,
         code: 401,
         description: 'Unauthorized, maybe invalid token.' } }
    

    如果一切顺利,机器人将连接上服务器并等待服务器的消息,提示如下:

    ➜  nodejsdemo node app.js
    Supported MessageSenders by SocketClient [ 'sendText',
      'sendImage',
      'sendVideo',
      'sendData',
      'sendSticker',
      'sendContact',
      'sendButton',
      'sendButtons',
      'sendApp' ]
    [ 'sendText',
      'sendImage',
      'sendVideo',
      'sendData',
      'sendSticker',
      'sendContact',
      'sendButton',
      'sendButtons',
      'sendApp' ]
    Message Received { id: '30e3c929-f6b7-46c2-9e46-6634af66daab',
      action: 'LIST_PENDING_MESSAGES' }
    

    打开Mixin Messenger,将你的机器人加为好友,(比如,这个机器人的 ID 是 7000101639) 然后就可以给它发消息了! 比如你发一个"hi"

    mixin_messenger

    终端将显示如下:

    Message Received { id: 'de4671c2-8873-419b-92b0-0d6ae8381940',
      action: 'LIST_PENDING_MESSAGES' }
    Message Received { id: 'a41816ca-2b65-4668-abdd-4526c1d29015',
      action: 'CREATE_MESSAGE',
      data:
       { type: 'message',
         representative_id: '',
         quote_message_id: '',
         conversation_id: 'c5458ec8-5e95-3e64-ae63-d4dfc3135c9e',
         user_id: '28ee416a-0eaa-4133-bc79-9676909b7b4e',
         message_id: 'a93ebfca-3d3f-44a9-9d63-3ad41ddca4b8',
         category: 'PLAIN_TEXT',
         data: 'hi',
         status: 'SENT',
         source: 'CREATE_MESSAGE',
         created_at: '2019-01-10T03:44:12.600158Z',
         updated_at: '2019-01-10T03:44:12.600158Z' } }
    Message Received { id: '810b93d9-56d4-413a-9837-6dc241e36ed0',
      action: 'ACKNOWLEDGE_MESSAGE_RECEIPT' }
    ignore receipt
    Message Received { id: 'd45c5139-8201-4f8a-aa2f-86c98ba3a849',
      action: 'CREATE_MESSAGE',
      data:
       { type: 'message',
         representative_id: '',
         quote_message_id: '',
         conversation_id: '',
         user_id: 'daf8b473-39a0-4419-991a-77f30d28dd6d',
         message_id: '9054acea-1a62-4716-9fa3-1a8c70a2165a',
         category: '',
         data: '',
         status: 'SENT',
         source: 'CREATE_MESSAGE',
         created_at: '2019-01-10T03:44:22.540536153Z',
         updated_at: '2019-01-10T03:44:22.540536153Z' } }
    Message Received { id: 'cf69c7a2-787b-4a91-be22-f51f38338179',
      action: 'ACKNOWLEDGE_MESSAGE_RECEIPT',
      data:
       { type: 'message',
         representative_id: '',
         quote_message_id: '',
         conversation_id: '',
         user_id: '',
         message_id: '9054acea-1a62-4716-9fa3-1a8c70a2165a',
         category: '',
         data: '',
         status: 'DELIVERED',
         source: 'ACKNOWLEDGE_MESSAGE_RECEIPT',
         created_at: '0001-01-01T00:00:00Z',
         updated_at: '2019-01-10T03:44:23.236843Z' } }
    ignore receipt
    Message Received { id: 'daa66945-abb6-4b8f-bc6a-04c4ccb6a837',
      action: 'ACKNOWLEDGE_MESSAGE_RECEIPT',
      data:
       { type: 'message',
         representative_id: '',
         quote_message_id: '',
         conversation_id: '',
         user_id: '',
         message_id: '9054acea-1a62-4716-9fa3-1a8c70a2165a',
         category: '',
         data: '',
         status: 'READ',
         source: 'ACKNOWLEDGE_MESSAGE_RECEIPT',
         created_at: '0001-01-01T00:00:00Z',
         updated_at: '2019-01-10T03:44:23.787562Z' } }
    ignore receipt
    

    下面对代码进行一个简单的解释

    机器人接受消息前,先建立到服务器的连接,再利用签名信息进行登陆认证。

    API 调用

    app.js

    const { SocketClient, isMessageType } = require('mixin-node-client');
    const config = require('./config');
    const client = new SocketClient(config);
    

    开启一个侦听,在这对收到的消息进行处理

    client.on(
      'message',
      client.getMessageHandler(message => {
        console.log('Message Received', message);
        return Promise.resolve(message);
      })
    );
    

    接收消息, 进行pay相关的逻辑处理

    if (ValidActions.indexOf(message.action) > -1) {
      if (message.action === 'ACKNOWLEDGE_MESSAGE_RECEIPT') {console.log("ignore receipt");return;}
    
      if (isMessageType(message, 'text')) {
        const text = message.data.data.toLowerCase();
        if ( (message.data.category === "PLAIN_TEXT") && (message.action === "CREATE_MESSAGE") ) {
          //todo: tell the server you got this message
          if (text === 'pay') {
            //todo: pay
          }
          return client.sendText(text, message);
        }
      }
      return Promise.resolve(message);
    } else console.log("unknow action")
    

    除了发送文本消息之外,还可以发送图片等消息,详细的消息类型请参考这里.

    对于每一条接收到的消息,将消息号( message_id)做为参数,回应服务器,action 为 ACKNOWLEDGE_MESSAGE_RECEIPT! 如果不回应,机器人下次登入,会重新获得消息。

    
        if ( (message.data.category === "PLAIN_TEXT") && (message.action === "CREATE_MESSAGE") ) {
        //  READ message start
            var parameter4IncomingMsg = {"message_id":message.data.message_id, "status":"READ"};
            var RspMsg = {"id":client.getUUID(), "action":"ACKNOWLEDGE_MESSAGE_RECEIPT", "params":parameter4IncomingMsg};
            client.sendRaw(RspMsg);
        // READ message end
          return client.sendText(text, message);
    

    最后, 执行 node app.js 将机器人上线!

    node app.js
    

    第二课 发送与接收加密货币

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