Gradle Custom Plugin Send Dingding Notification

In the previous article, we introduced how to use the Gradle plugin, apk reinforcement, and upload to Dandelion.

This article is mainly to further improve the process, through the Gradle plug-in to achieve: packaging - reinforcement - upload dandelion - send DingTalk message , to achieve complete automation.

Introduction to previous articles:

Gradle Custom Plugin Introduction

Gradle Custom Plugin Upload APK to Dandelion

Gradle Custom Plugin 360 Reinforcement

Next, let's look at the simple process of sending DingTalk notifications:

  • Create a DingTalk robot. Get credentials for sending messages.
  • Call the API according to the data format required by the credentials and DingTalk

DingTalk's API documentation contains detailed process descriptions, so I won't go into details here.

Develop DingTalk message Task task flow:

1. Create a DingTalk robot

To send DingTalk messages, we need to create a DingTalk bot in the DingTalk group. After creating the DingTalk robot, send messages to the DingTalk group through the robot's Webhook and secret.

1. Click group smart group assistant

insert image description here

2. Click Create Robot

Click to add a robot (permission may be required). This requires computer operation. After creating the robot, you will get the Webhook and secret.
These two parameters are what we need to send data.

insert image description here
After creating the robot, we got the webhook and secret, which are the credentials for sending DingTalk messages.
insert image description here

After creating the robot, we can call the API through the credentials according to the API documentation of DingTalk . Here I do not use the official SDK, but send messages through OKHttp according to the format of the document.

2. Create a Task task for sending DingTalk messages

According to the documentation, we know that to send DingTalk, we must pass the parameters: webhook, secret .
Then, select different parameters according to the type of message we send, here we use the link type to send messages:

{
    "msgtype": "link", 
    "link": {
        "text": "这个即将发布的新版本,创始人xx称它为“红树林”。
而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是“红树林”?", 
        "title": "时代的火车向前开", 
        "picUrl": "", 
        "messageUrl": "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI"
    }
}

Here we see that we need to upload: text, title, picUrl, messageUrl , the four parameters
picUrl, link messageUrl, must be obtained after we upload to Dandelion.

In this case, we need Extension to add 4 parameters: webhook, secret, text, title . The other two parameters (picUrl, messageUrl) need to be obtained after uploading Dandelion successfully.

After the above parameters are ready, we only need to call the API through OKHttp to complete.

Preparations include:

  • Build a Model class that meets the needs of DingTalk
  • Add parameters to Extension (the four parameters analyzed above)
  • Get the Url parameter after uploading Dandelion successfully
  • Parsing the parameters of the Extension
  • Call API to send DingTalk message

1. Build a Model class that meets the needs of DingTalk

Sending DingTalk is delivered through Json, we first build the model it needs

public static class DingTalkModel {

        public String picUrl;
        public String messageUrl;
        public String title;
        public String text;
    }

    public static class DingTalkRequest {

        public String msgtype = "link";
        public DingTalkModel link;

        public DingTalkRequest(DingTalkModel link) {
            this.link = link;
        }
    }

2. Add parameters to Extension

There are mainly four parameters here: upload credentials (webhook and secret), send message parameters (title, content).

Add parameters to the Extension class.

public class Extension {


  public String uKey;
  public String apiKey;
  public String appName;

  //==========加固相关的信息
//指向加固的jar包
  public File reinforceJarFile;
  //登陆用户名
  public String reinforceUsername;
  //登陆密码
  public String reinforcePassword;
  //输出apk的目录
  public File outputFile;

  //dingding
  public String dingWebhook;
  public String dingSecret;
  public String title;
  public String content;

  public Extension() {
  }

  public Extension(String uKey, String apiKey, String appName, File reinforceJarFile,
      String reinforceUsername, String reinforcePassword, File outputFile,
      String dingWebhook, String dingSecret, String title, String content) {
    this.uKey = uKey;
    this.apiKey = apiKey;
    this.appName = appName;
    this.reinforceJarFile = reinforceJarFile;
    this.reinforceUsername = reinforceUsername;
    this.reinforcePassword = reinforcePassword;
    this.outputFile = outputFile;
    this.dingWebhook = dingWebhook;
    this.dingSecret = dingSecret;
    this.title = title;
    this.content = content;
  }

  public Extension(String uKey, String apiKey, String appName, File reinforceJarFile,
      String reinforceUsername, String reinforcePassword, File outputFile) {
    this.uKey = uKey;
    this.apiKey = apiKey;
    this.appName = appName;
    this.reinforceJarFile = reinforceJarFile;
    this.reinforceUsername = reinforceUsername;
    this.reinforcePassword = reinforcePassword;
    this.outputFile = outputFile;
  }
  public Extension(String appName, String uKey, String apiKey) {
    this.uKey = uKey;
    this.apiKey = apiKey;
    this.appName = appName;
  }
  public static Extension getConfig(Project project) {
    Extension extension = project.getExtensions().findByType(Extension.class);
    if (extension == null) {
      extension = new Extension();
    }
    return extension;
  }

}


3. Obtain the Url parameter after uploading Dandelion successfully

We define static properties in the DingTalk task. When the Dandelion upload task is completed, call the setData() method to pass the parameters.

public class DingTalkTask extends DefaultTask {

    private static String sShotcutUrl, sQRCodeURL;
	...

    public static void setData(String shortcutUrl, String qrUrl) {
        sShotcutUrl = shortcutUrl;
        sQRCodeURL = qrUrl;
    }
}

4. Parse the parameters of the Extension

Here is the preparation of the parameters before networking. We need to parse out the required parameters here and put them in the model class.

   private DingTalkModel initModel() {
        DingTalkModel model = new DingTalkModel();
        model.picUrl = sQRCodeURL;
        model.messageUrl = "http://www.pgyer.com/" + sShotcutUrl;
        Extension extension =Extension.getConfig(mTargetProject);
        model.title = extension.title;
        model.text = extension.content;

        mWebhook = extension.dingWebhook;
        mSecret = extension.dingSecret;
        return model;
    }

Create the model required by DingTalk here, and then get all the corresponding parameters.

5. Call API to send DingTalk message

This, call directly using OKHttp


    @TaskAction
    public void sendDingtalk() {
        DingTalkModel model = initModel();

        String url = createDingUrl();

        OkHttpClient okHttpClient = new OkHttpClient();
        Gson gson = new Gson();
        String json = gson.toJson(new DingTalkRequest(model));
        RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
            , json);
        Request request = new Request.Builder()
            .url(url)
            .post(requestBody)
            .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            System.out.println("send message result: " + response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * create dingtalk  url
     */
    private String createDingUrl() {
        Long timestamp = System.currentTimeMillis();
        return mWebhook + "&timestamp=" + timestamp + "&sign=" + createSign(timestamp, mSecret);
    }

   private String createSign(Long timestamp, String secret) {
        String stringToSign = timestamp + "\n" + secret;
        Mac mac = null;
        try {
            mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
            return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }


At this point, our DingTalk Task is created. Post the complete code.


public class DingTalkTask extends DefaultTask {

    private static String sShotcutUrl, sQRCodeURL;
    private BaseVariant mVariant;
    private Project mTargetProject;

    private String mWebhook;
    private String mSecret;

    public static class DingTalkModel {

        public String picUrl;
        public String messageUrl;
        public String title;
        public String text;
    }

    public static class DingTalkRequest {

        public String msgtype = "link";
        public DingTalkModel link;

        public DingTalkRequest(DingTalkModel link) {
            this.link = link;
        }
    }

    public void init(BaseVariant variant, Project project) {
        this.mVariant = variant;
        this.mTargetProject = project;
        setDescription("send message to ding talk");
        setGroup(TestJavaPlugin.PLUGIN_EXTENSION_NAME);
    }

    @TaskAction
    public void sendDingtalk() {
        DingTalkModel model = initModel();

        String url = createDingUrl();

        OkHttpClient okHttpClient = new OkHttpClient();
        Gson gson = new Gson();
        String json = gson.toJson(new DingTalkRequest(model));
        RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
            , json);
        Request request = new Request.Builder()
            .url(url)
            .post(requestBody)
            .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            System.out.println("send message result: " + response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * create dingtalk  url
     */
    private String createDingUrl() {
        Long timestamp = System.currentTimeMillis();
        return mWebhook + "&timestamp=" + timestamp + "&sign=" + createSign(timestamp, mSecret);
    }

    private DingTalkModel initModel() {
        DingTalkModel model = new DingTalkModel();
        model.picUrl = sQRCodeURL;
        model.messageUrl = "http://www.pgyer.com/" + sShotcutUrl;
        Extension extension =Extension.getConfig(mTargetProject);
        model.title = extension.title;
        model.text = extension.content;

        mWebhook = extension.dingWebhook;
        mSecret = extension.dingSecret;
        return model;
    }

    private String createSign(Long timestamp, String secret) {
        String stringToSign = timestamp + "\n" + secret;
        Mac mac = null;
        try {
            mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
            return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    public static void setData(String shortcutUrl, String qrUrl) {
        sShotcutUrl = shortcutUrl;
        sQRCodeURL = qrUrl;
    }
}

3. Modify and upload the Dandelion Task task

As mentioned above, after the upload is complete, we need to pass the URL to the DingTalk Task task.

public class PGYUploadTask extends DefaultTask {

    private BaseVariant mVariant;
    private Project mTargetProject;

    public static class PGYRequest {

        public String uKey;
        public String apiKey;
        //1,install by public 2,install by password 3,install by invite
        public String installType;
    }

    public void init(BaseVariant variant, Project project) {
        this.mVariant = variant;
        this.mTargetProject = project;
        setDescription("upload to pgy");
        setGroup(TestJavaPlugin.PLUGIN_EXTENSION_NAME);
    }

    @TaskAction
    public void uploadToPGY() {
        Extension extension = Extension.getConfig(mTargetProject);

        PGYRequest request = new PGYRequest();
        request.apiKey = extension.apiKey;
        request.uKey = extension.uKey;
        File apkDir = extension.outputFile;
        if (apkDir == null || !apkDir.exists()) {
            upload(request);
        } else {
            File[] files = apkDir.listFiles();
            if (files != null && files.length > 0) {
                upload(request.uKey, request.apiKey, files[0]);
            } else {
                upload(request);
            }
        }
    }

    private void upload(PGYRequest request) {
        for (BaseVariantOutput output : mVariant.getOutputs()) {
            File file = output.getOutputFile();
            if (file == null || !file.exists()) {
                throw new GradleException("apk file is not exist!");
            }
            upload(request.uKey, request.apiKey, file);
        }
    }

    private void upload(String ukey, String apiKey, File apkFile) {
        //builder
        MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        //add part
        bodyBuilder.addFormDataPart("uKey", ukey);
        bodyBuilder.addFormDataPart("_api_key", apiKey);
        //add file
        bodyBuilder.addFormDataPart("file", apkFile.getName(), RequestBody
            .create(MediaType.parse("*/*"), apkFile));

        //request

        Request request = new Request.Builder()
            .url("http://upload.pgyer.com/apiv1/app/upload")
            .post(bodyBuilder.build())
            .build();

        OkHttpClient client = new OkHttpClient();
        try {
            Response response = client.newCall(request).execute();
            String result = response.body().string();
            System.out.println("upload result: " + result);
            parseResult(result);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parseResult(String result) {
        if (result == null || result.length() == 0) {
            throw new IllegalArgumentException("upload apk to PGY failed");
        }
        PGYResonpse data = new Gson().fromJson(result, PGYResonpse.class);
        if (data == null || data.code != 0) {
            throw new IllegalArgumentException("upload apk to PGY failed");
        }
        DingTalkTask.setData(data.data.appShortcutUrl, data.data.appQRCodeURL);
    }

    public static class PGYResonpse {

        public int code;
        public String message;
        public PGYDetail data;

        public static class PGYDetail {

            public String appShortcutUrl;
            public String appUpdated;
            public String appQRCodeURL;
            public String appVersion;
            public String appVersionNo;
            public String appIcon;

        }
    }

}

Here, first create the Model class for the results returned by Dandelion; then, after obtaining the uploaded Dandelion results, pass the data to the DingTalk Task task through the DingTalkTask.setData() method.

Fourth, modify the dependencies of the tasks in the Plugin plug-in

Here, it is mainly to modify the task dependencies in the Plugin plug-in. Let DingTalk's Task depend on the task of uploading Dandelion, so that DingTalk notification will be sent after the upload is successful.

public class TestJavaPlugin implements Plugin<Project> {

    public static final String PLUGIN_EXTENSION_NAME = "uploadHelperJava";
    public static final String ANDROID_EXTENSION_NAME = "android";


    @Override
    public void apply(Project project) {
        project.getExtensions()
            .create(PLUGIN_EXTENSION_NAME, Extension.class);
        //项目编译完成后,回调
        project.afterEvaluate(new Action<Project>() {
            @Override
            public void execute(Project project) {
                DomainObjectSet<ApplicationVariant> appVariants = ((AppExtension) project
                    .getExtensions().findByName(ANDROID_EXTENSION_NAME)).getApplicationVariants();
                for (ApplicationVariant variant : appVariants) {
                    //release apk
                    if (variant.getBuildType().getName().equalsIgnoreCase("uploadRelease")) {
                        String variantName =
                            variant.getName().substring(0, 1).toUpperCase() + variant.getName()
                                .substring(1);

                        //create task
                        ReinforceTask reinforceTask = project.getTasks()
                            .create("reinforceFor" + variantName, ReinforceTask.class);
                        reinforceTask.init(variant, project);

                        PGYUploadTask uploadTask = project.getTasks()
                            .create("uploadJavaFor" + variantName, PGYUploadTask.class);
                        uploadTask.init(variant, project);

                        DingTalkTask talkTask = project.getTasks()
                            .create("dingTalkFor" + variantName, DingTalkTask.class);
                        talkTask.init(variant, project);

                        //depend on
                        variant.getAssembleProvider().get()
                            .dependsOn(project.getTasks().findByName("clean"));
                        reinforceTask.dependsOn(variant.getAssembleProvider().get());
                        uploadTask.dependsOn(reinforceTask);
                        talkTask.dependsOn(uploadTask);

                    }
                }
            }
        });
    }
}

Here, we also created the DingTalk Task. Then, the dependencies were modified. At this point, our entire process: packaging – strengthening – uploading Dandelion – sending DingTalk notifications is complete.

Five, call

The release of plug-ins and the introduction of plug-ins have been mentioned in previous articles (there is a link at the beginning), so I won’t write them here.

Let's look at the build.gradle under the module of the app that needs to call the plug-in

apply plugin: 'com.android.application'

android {
    ......
    signingConfigs {
        release {
            storeFile file('xxx.jks')
            storePassword "xxx"
            keyAlias "xxx"
            keyPassword "xxx"
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        uploadRelease {
            minifyEnabled false
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

......

apply plugin: 'com.liu.alone.plugin'
uploadHelperJava {
    appName = "测试APP"

    uKey = "xxxx"

    apiKey = "xxxx"

    //加固相关的信息
    reinforceJarFile = file("../libs/jiagu.jar")
    reinforceUsername = "18032793516"
    reinforcePassword = "san7758521"
    outputFile = file("${buildDir.getAbsolutePath()}\\jiagu")

    //钉钉,这里需要修改成自己的值
    dingWebhook = "https://oapi.dingtalk.com/robot/send?access_token=xxx"
    dingSecret= "xxx"
    title = "新版本APP上传完成"
    content="体验新版本APP,请到蒲公英下载"

}


Task call:

insert image description here

Print result:

> Task :app:reinforceForUploadRelease
==============start JiaGu
....
任务完成_已签名


> Task :app:uploadJavaForUploadRelease
upload result: {"code":0,"message":"","data":{"appKey":"3e95fe0e7fa74e9f7a310bbdfbd139cc","userKey":"c9d2625c0cf221d8f4a98738f4c05e9a","appType":"2","appIsLastest":"2","appFileSize":"3264349","appName":"PluginTest","appVersion":"1.0","appVersionNo":"1","appBuildVersion":"4","appIdentifier":"com.liu.plugintest","appIcon":"9664daaa8a725f726bcb6018495f14a7","appDescription":"","appUpdateDescription":"","appScreenshots":"","appShortcutUrl":"DqmH","appCreated":"2020-01-03 12:21:24","appUpdated":"2020-01-03 12:21:24","appQRCodeURL":"http:\/\/www.pgyer.com\/app\/qrcodeHistory\/9c27fd62288ce774fafb622d8f6df0bbcd9f90cf0b9ba39846c1c6864c93b135"}}

> Task :app:dingTalkForUploadRelease
send message result: {"errcode":0,"errmsg":"ok"}

Here, we see that three tasks have been executed successively: Task :app:reinforceForUploadRelease, Task :app:uploadJavaForUploadRelease, Task :app:dingTalkForUploadRelease

Finally, DingTalk notification
insert image description here

At this point, our entire automated process of testing releases: packaging—reinforcement—uploading Dandelion—sending DingTalk notifications is realized by customizing the Gradle plug-in.

other documents

plugin-build.gradle

apply plugin: 'java-library'
apply plugin: 'maven'
dependencies {
    compile gradleApi()
    compile localGroovy()
    compile 'com.android.tools.build:gradle:3.3.1'
    implementation("com.squareup.okhttp3:okhttp:3.8.1")
}
repositories {
    mavenCentral()

}

group = 'com.liu.alone.plugin'
version = '1.0.0'
archivesBaseName = 'java-plugin'
//
//upload
uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri('../repo'))
        }
    }
}

Guess you like

Origin blog.csdn.net/ecliujianbo/article/details/103818432