Skip to content

Latest commit

 

History

History
317 lines (282 loc) · 12.4 KB

README-zhchs.md

File metadata and controls

317 lines (282 loc) · 12.4 KB

基于Mixin Network的Java比特币开发教程

cover

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

课程简介

  1. 创建一个接受消息的机器人
  2. 机器人接受比特币并立即退还用户

安装Java

如果你运行的是macOS, 手动到此下载JDK12, 下载完成后,双击 jdk-11.0.2_osx-x64_bin.dmg, 在弹出的新窗口中,点击 JDK 11.0.2.pkg文件,依提示一步一步完成安装, Java会安装在 /Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home/bin/ 目录中,将这个路径加入到$PATH

echo 'export PATH=/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home/bin/:$PATH' >> ~/.bash_profile
source ~/.bash_profile

安装成功后,执行 java --version 将得到如下信息:

wenewzha:mixin_labs-java-bot wenewzhang$ java --version
java 11.0.2 2019-01-15 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.2+9-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.2+9-LTS, mixed mode)

Ubuntu

apt update
apt upgrade
apt install unzip
java --version

以Ubuntu 16.04为例, openjdk版本的Java已经安装好了, 执行 java --version来验证安装情况。

root@ubuntu:~# java --version
openjdk 10.0.2 2018-07-17
OpenJDK Runtime Environment (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4)
OpenJDK 64-Bit Server VM (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4, mixed mode)

在操作系统中安装最新版本的Gradle

本教程采用Gradle来构建,你可从下面的地址来下载安装!Gradle下载

macOS

brew update
brew install gradle

Ubuntu下的Gradle太旧,我们手动安装它:

cd ~/Downloads
wget https://services.gradle.org/distributions/gradle-5.1.1-bin.zip
unzip gradle-5.1.1-bin.zip

解压gradle-5.1.1-bin.zip后,增加安装目录到$PATH中:

echo 'export PATH=/root/gradle-5.1.1/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

当Gradle安装成功后, 执行gradle -v来验证安装情况:

root@ubuntu:~# gradle -v
------------------------------------------------------------
Gradle 5.1.1
------------------------------------------------------------
...

创建第一个机器人APP

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

生成相应的参数

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

mixin_network-keys

Hello,World!

进入到你的工作目录,创建mixin_labs-java-bot目录, 执行gradle init来生成基本信息资料.

gradle init --dsl kotlin --type java-application --test-framework junit --project-name mixin_labs-java-bot

进入src/main/java/mixin_labs/java/bot目录,新建一个Config.java, 填写如下内容:

Config.java

package mixin_labs.java.bot;
import mixin.java.sdk.MixinUtil;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.util.Base64;
import mixin.java.sdk.PrivateKeyReader;
public class Config {

public static final String CLIENT_ID     = "b1ce2967-a534-417d-bf12-c86571e4eefa";
public static final String CLIENT_SECRET = "e6b14c6bbb20a43c603c468e225e6e4c666c940792cde43e41b34c3f1dd45713";
public static final String PIN           = "536071";
public static final String SESSION_ID    = "2f1c44a3-d4d2-4dd2-bdb6-8eda67694b91";
public static final String PIN_TOKEN     = "ajJJngHmWgIfH3S2mgH4bAsoPeoXV6hI1KoTZW9AvFUK1R8e28X1zVRCcrOMVeXkvBKQeEMgRdX1kRgH3ksITTBm2mgK5eUnfBHUuRC85oKoQGB9e2Bp4O4ZKGg/6bqLeD66pnBPcO2s7VtgLSAK0tHa2jMzmGlWuxsO6Wo5JHE=";

  private static RSAPrivateKey loadPrivateKey() {
    try {

      PrivateKey key =
        new PrivateKeyReader(Config.class.getClassLoader().getResourceAsStream("rsa_private_key.txt"))
          .getPrivateKey();
      System.out.println(key);
      return (RSAPrivateKey) key;
    } catch (Exception e) {
      e.printStackTrace();
      System.exit(1);
      return null;
    }
  }

  public static final RSAPrivateKey RSA_PRIVATE_KEY = loadPrivateKey();
  public static final byte[] PAY_KEY = MixinUtil.decrypt(RSA_PRIVATE_KEY, PIN_TOKEN, SESSION_ID);
}

用你创建的APP的参数,替换文件中的内容: CLIENT_ID, client_id, CLIENT_SECRET, and the PIN, PIN_TOKEN, SESSION_ID.

创建App.java文件,内容如下:

App.java

/*
 * This Java source file was generated by the Gradle 'init' task.
 */
package mixin_labs.java.bot;
import mixin.java.sdk.MixinBot;
import mixin.java.sdk.MixinUtil;
import mixin.java.sdk.MIXIN_Category;
import mixin.java.sdk.MIXIN_Action;

import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateKey;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
// import java.util.Base64;
import org.apache.commons.codec.binary.Base64;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;


public class App {

    public static void main(String[] args) {
        MixinBot.connectToRemoteMixin(new WebSocketListener() {
        @Override
        public void onOpen(WebSocket webSocket, Response response) {
          System.out.println("[onOpen !!!]");
          System.out.println("request header:" + response.request().headers());
          System.out.println("response header:" + response.headers());
          System.out.println("response:" + response);

          // 请求获取所有 pending 的消息
          MixinBot.sendListPendingMessages(webSocket);
        }

        @Override
        public void onMessage(WebSocket webSocket, String text) {
          System.out.println("[onMessage !!!]");
          System.out.println("text: " + text);
        }

        @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) {
          try {
            System.out.println("[onMessage !!!]");
            String msgIn = MixinUtil.bytesToJsonStr(bytes);
            System.out.println("json: " + msgIn);
            JsonObject obj = new JsonParser().parse(msgIn).getAsJsonObject();
            MIXIN_Action action = MIXIN_Action.parseFrom(obj);
            System.out.println(action);
            MIXIN_Category category = MIXIN_Category.parseFrom(obj);
            System.out.println(category);
            if (action == MIXIN_Action.CREATE_MESSAGE && obj.get("data") != null &&
                category != null ) {
              String userId;
              String messageId = obj.get("data").getAsJsonObject().get("message_id").getAsString();
              MixinBot.sendMessageAck(webSocket, messageId);
              switch (category) {
                case PLAIN_TEXT:
                    String conversationId =
                      obj.get("data").getAsJsonObject().get("conversation_id").getAsString();
                    userId =
                      obj.get("data").getAsJsonObject().get("user_id").getAsString();
                    byte[] msgData = Base64.decodeBase64(obj.get("data").getAsJsonObject().get("data").getAsString());
                    MixinBot.sendText(webSocket,conversationId,userId,new String(msgData,"UTF-8"));
                    break;
                default:
                    System.out.println("Category: " + category);
              }
            }
          } catch (Exception e) {
            e.printStackTrace();
          }
        }

        @Override
        public void onClosing(WebSocket webSocket, int code, String reason) {
          System.out.println("[onClosing !!!]");
          System.out.println("code: " + code);
          System.out.println("reason: " + reason);
        }

        @Override
        public void onClosed(WebSocket webSocket, int code, String reason) {
          System.out.println("[onClosed !!!]");
          System.out.println("code: " + code);
          System.out.println("reason: " + reason);
        }

        @Override
        public void onFailure(WebSocket webSocket, Throwable t, Response response) {
          System.out.println("[onFailure !!!]");
          System.out.println("throwable: " + t);
          System.out.println("response: " + response);
        }
      }, Config.RSA_PRIVATE_KEY, Config.CLIENT_ID, Config.SESSION_ID);
    }
}

进入src/main/resources, 新建文件:rsa_private_key.txt, 填写私钥信息:

rsa_private_key.txt

-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----

本教程依赖 mixin-java-sdk 现在回到项目目录,从github下载mixin-java-sdk

mkdir libs
cd libs
wget https://github.com/wenewzhang/mixin-java-sdk/releases/download/v2/mixin-java-sdk.jar

增加依赖到build.gradle.kts,增加编译文件compile(files("libs/mixin-java-sdk.jar")), 完整的依赖包如下:

dependencies {
    // This dependency is found on compile classpath of this component and consumers.
    implementation("com.google.guava:guava:26.0-jre")
    // dependent on mixin-java-sdk, copy it to libs directory
    compile(files("libs/mixin-java-sdk.jar"))
    implementation("commons-codec:commons-codec:1.11")
    implementation("com.auth0:java-jwt:3.5.0")
    implementation("com.squareup.okio:okio:2.2.1")
    implementation("com.squareup.okhttp3:okhttp:3.12.1")
    implementation("com.google.code.gson:gson:2.8.5")
    // Use JUnit test framework
    testImplementation("junit:junit:4.12")
}

进入到 src/test/java/mixin_labs/java/bot, 注释掉下面的代码

AppTest.java

        // assertNotNull("app should have a greeting", classUnderTest.getGreeting());

最后一步,回到项目目录mixin_labs-java-bot, 编译并运行.

gradle build
gradle run

如果你看到如下信息,表示已经连接成功了,机器人小程序已经就绪,你可以发信息给他了!

response:Response{protocol=http/1.1, code=101, message=Switching Protocols, url=https://blaze.mixin.one/}
[onMessage !!!]
json: {"id":"4ee01b68-817e-4f29-bcb4-b40f7c163f61","action":"LIST_PENDING_MESSAGES"}
LIST_PENDING_MESSAGES

mixin_messenger 信息如上图所示!

源代码解释

MixinBot.connectToRemoteMixin(new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
  MixinBot.sendListPendingMessages(webSocket);
}

连接到Mixin Network并发送"LISTPENDINGMESSAGES"消息,服务器以后会将收到的消息转发给此程序!

消息处理回调函数

        public void onMessage(WebSocket webSocket, ByteString bytes) {
          try {
            System.out.println("[onMessage !!!]");
            String msgIn = MixinUtil.bytesToJsonStr(bytes);

当服务器给机器人推送消息的时候,机器人的onMessage函数会被调用

发送消息响应

String messageId = obj.get("data").getAsJsonObject().get("message_id").getAsString();
MixinBot.sendMessageAck(webSocket, messageId);

机器人收到消息后需要发送响应给服务器,这样服务器就知道消息已经收到,不会再发一遍

消息回应

switch (category) {
  case PLAIN_TEXT:
      String conversationId =
        obj.get("data").getAsJsonObject().get("conversation_id").getAsString();
      userId =
        obj.get("data").getAsJsonObject().get("user_id").getAsString();
      byte[] msgData = Base64.decodeBase64(obj.get("data").getAsJsonObject().get("data").getAsString());
      MixinBot.sendText(webSocket,conversationId,userId,new String(msgData,"UTF-8"));

好了,你的第一个机器人小程序已经运行起来了, 你有什么新的想法,来试试吧!

完整的代码 这儿