[程序员] Java 收发比特币的中文教程之一: 创建一个 Mixin Network 应用

新闻来源    2019年02月04日 08:02

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 中.

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

源代码解释

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"));

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

完整的代码 这儿


新闻来源


CryptoCurrencyCNYChange 1hChange 24hChange 7d
Bitcoin33,872 0.26 % 1.07 % 0.13 %
Ethereum1,223.5 0.71 % 0.20 % 4.31 %
XRP2.070 0.71 % 0.51 % 9.03 %
Tether7.090 0.09 % 0.09 % 0.31 %
Bitcoin Cash1,509.9 0.67 % 0.13 % 3.26 %
Litecoin381.95 0.17 % 1.42 % 3.09 %
EOS20.36 0.65 % 0.51 % 6.16 %
Binance Coin197.40 2.02 % 8.22 % 11.83 %
Bitcoin SV641.35 0.70 % 0.44 % 8.29 %
Stellar0.4475 0.46 % 0.88 % 4.77 %
Cardano0.2764 0.35 % 3.20 % 2.99 %
TRON0.1103 0.80 % 3.25 % 1.31 %
LEO Token7.180 0.66 % 1.17 % 1.59 %
Monero461.20 0.03 % 1.56 % 3.38 %
ChainLink16.73 0.13 % 1.54 % 13.98 %
Huobi Token24.34 0.11 % 0.31 % 3.28 %
IOTA1.950 0.22 % 1.98 % 3.23 %
Tezos6.210 0.08 % 0.09 % 2.78 %
OKB16.48 0.24 % 0.27 % 4.83 %
Cosmos19.64 0.42 % 0.24 % 1.47 %
Dash805.40 0.20 % 1.77 % 9.06 %
Ethereum Classic38.85 0.12 % 1.75 % 3.58 %
NEO50.36 0.48 % 0.20 % 1.49 %
USD Coin7.090 0.02 % 0.03 % 0.17 %
Ontology4.480 0.22 % 2.26 % 0.64 %
Maker4,974.6 0.05 % 0.47 % 3.56 %
NEM0.2884 0.33 % 0.31 % 4.36 %
Crypto.com Coin0.2357 0.08 % 0.51 % 3.24 %
Dogecoin0.01718 0.39 % 0.83 % 3.09 %
Basic Attention Token1.530 0.39 % 3.93 % 13.84 %