From eb0bd17432fbd71b11c6e55d78d79ed508350d27 Mon Sep 17 00:00:00 2001
From: hk <hk123456.com>
Date: Thu, 09 Jan 2025 11:11:27 +0800
Subject: [PATCH] 百度打标签

---
 rpa/src/js/baidu_unlogin_tag1.js |  437 +++++++++++++++++++++++++++++++++++++++
 rpa/src/slib/actions.js          |  176 +++++++++++++--
 2 files changed, 590 insertions(+), 23 deletions(-)

diff --git a/rpa/src/js/baidu_unlogin_tag1.js b/rpa/src/js/baidu_unlogin_tag1.js
new file mode 100644
index 0000000..6e65f31
--- /dev/null
+++ b/rpa/src/js/baidu_unlogin_tag1.js
@@ -0,0 +1,437 @@
+/**
+ * description 抖音打标签1  搜索关键词,随机浏览推荐页作品,命中关键词作品停留
+ * @搜索关键词, 命中关键词, 标题关键词
+ * @type {launch|{}}
+ */
+let launch = require("slib/launch"), actions = require("slib/actions"), dynamicData = require("slib/dynamicData"),
+    request = require("slib/request"), swiper = require("slib/swiper");
+let ecSdk = require('slib/EASYCLICKSDK')
+let scriptVersion = "2025.01.06"
+logd("《chk百度打标签1》启动成功,版本:" + scriptVersion);
+
+let storage = storages.create("storage"); //创建存储对象
+storage.clear(); //清空存储
+
+let platform = 4; //运行平台: 1抖音  2小红书  3快手 4百度 5QQ浏览器
+let taskInfo = request.getTask(); //获取任务信息
+if (taskInfo.tenantId) storage.putInt("tenantId", taskInfo.tenantId); //存储租户id
+
+storage.putString("serial_num", time()); //存储任务流水号(生成时间戳)
+
+let searchKeywords, hitKeywords;  //搜索关键词, 命中关键词;
+let label; //账号标签(同搜索内容)
+let total = 3; //每次目标数
+let gjcNum = 0;
+let max_views = random(50, 60); //浏览作品数
+let taskId = launch.taskThread(random(30, 40)); //开启任务时间线程 30分钟
+let taskNums = random(10,15) // 町停时间 秒
+let taskSwNum = random(3,5)
+// taskInfo.valueJson = {
+//     'baidu-hk标题关键词':'hk标题关键词',
+//     'baidu-hk搜索关键词':'医美|植发|假体'
+// }
+if (taskInfo.valueJson) {
+    logd('获取任务数据:' + JSON.stringify(taskInfo.valueJson));
+    dynamicData.deviceLog(false, {log_detail: '获取任务数据:' + JSON.stringify(taskInfo.valueJson)});
+    //是否有任务参数
+    if (taskInfo.valueJson['baidu-hk标题关键词']) label = taskInfo.valueJson['baidu-hk标题关键词'].split("|");
+    logd('获取任务参数:标题关键词,' + JSON.stringify(label));
+    //是否有需求数据,没有则使用数组名称获取
+    if (taskInfo.valueJson['baidu-hk搜索关键词']) {
+        searchKeywords = taskInfo.valueJson['baidu-hk搜索关键词'].split("|");
+        logd('获取任务参数:搜索关键词,' + JSON.stringify(searchKeywords))
+    }
+
+} else {
+    logd('没有任务参数');
+    dynamicData.deviceLog(false, {log_detail: '没有任务参数'});
+}
+let taskType = 2 //1 运行模式 2 无障碍模式
+launch.launchDevice(taskId, taskType); //唤醒设备
+
+main()
+
+function main() {
+    // 1 关键词
+    // keyword
+    // 2 文章观看停留时长
+    // random()
+    // 3 浏览推荐时长
+    // time
+    // 4 换标签 时长
+    // time
+    // 5 总运行时长
+    // time
+    ecSdk.msg('程序执行')
+    let hasLaunchApp = launch.launchApp(4, 15000, 3); //打开百度APP
+    if (hasLaunchApp) {
+        launch.alertListener(4, taskId); //处理弹窗
+    }
+    //首页 处理逻辑
+
+    //&&isHome()
+    let gjNum = 0 //广告出现次数
+    while (!thread.isCancelled(taskId)) {
+        try {
+            //
+            logd('获取搜索框')
+            if (gjNum >= searchKeywords.length) {
+                gjNum = 0
+            }
+            let keyword = searchKeywords[gjNum];
+            let hasSearch = actions.searchByKeyword(keyword, 4); //通过schema打开openActivity界面,搜索关键词
+            if (hasSearch) {//需要判断是不是首页
+                logd('找到搜索框并点击成功,结果页找广告')
+                // result = true;
+                sleep(random(10, 30) * 100);
+                // actions.videoSwiper()
+                //
+
+                //结果页 需要翻页 多次查询
+                let nodeData = text("广告").pkg('com.baidu.searchbox').getOneNodeInfo(3 * 1000)
+                if (nodeData && nodeData != undefined) {
+                    //找到需要点击广告 节点
+                    let parentNode = nodeData.parent().parent()
+                    if (parentNode && parentNode.click()) {
+                        //等待
+                        // randomSleep()
+                        sleep(taskNums * 1000);
+                        for (let i = 0; i < taskSwNum; i++) {
+                            actions.videoSwiper()
+                            sleep(1000);
+                        }
+                        // 返回到二级页面
+                        isHome()
+                    } else {
+                        // 没有找到
+                        //华东
+                        actions.videoSwiper(null);
+                        back()
+                    }
+                } else {
+                    logd('没有找到广告')
+                    // actions.videoSwiper(null);
+                    // actions.videoSwiper(null);
+                    for (let i = 0; i < 3; i++) {
+                        actions.videoSwiper()
+                        randomSleep()
+                    }
+                    let tjNode = clz('android.widget.Button').getOneNodeInfo(3 * 1000) //text("大家还在搜").pkg('com.baidu.searchbox').getOneNodeInfo(3 * 1000)
+                    if (tjNode && tjNode.click()) {
+                        // let button = tjNode.parent().allChildren().clz('android.widget.Button').getOneNodeInfo('3000')
+                        // if (button) {
+                        //     button.click()
+                        // } else {
+                        //     toast('没有找到推荐 并点击')
+                        for (let i = 0; i < 2; i++) {
+                            actions.videoSwiper()
+                            randomSleep()
+                        }
+                        isHome()
+                        // }
+                    } else {
+                        toast('没有找到大家还在搜')
+                        isHome()
+                    }
+                    //滑动
+                }
+                gjNum += 1
+            } else {
+                logd('不是首页去首页');
+                isHome()
+            }
+        } catch (e) {
+            loge('发生错误:' + e)
+            isHome()
+            randomSleep()
+        }
+    }
+}
+
+//
+function checkGg() {
+    let flag = false
+    let nodeData = text("广告").pkg('com.baidu.searchbox').getOneNodeInfo(3 * 1000)
+    if (nodeData && nodeData != undefined) {
+        //找到需要点击广告 节点
+        let parentNode = nodeData.parent().parent()
+        if (parentNode && parentNode.click()) {
+            sleep(taskNums * 1000);
+            for (let i = 0; i < taskSwNum; i++) {
+                actions.videoSwiper()
+                sleep(1000);
+            }
+            flag = true
+
+        }
+    }
+    return flag
+}
+
+function isHome() {
+    let result = false;
+    while (true) {
+        var home_selectors = text("百度").getOneNodeInfo(3000); //首页按钮
+        // var message_selectors = text("消息").getOneNodeInfo(3000); //消息按钮
+        //判断是否在首页
+        if (home_selectors) {
+            var follow_selectors = text("好看视频"); //关注按钮
+            // var recommend_selectors = text("推荐"); //推荐按钮
+            if (has(follow_selectors)) {
+                toast('已定位首页');
+                // randomSleep("已定位到首页", "定位到首页", "", "", "", "");
+                result = true;
+                break;
+            } else {
+                home_selectors.click();
+                sleep(3000);
+            }
+        } else {
+            toast('返回首页...');
+            logd('返回首页...');
+            //返回上一页
+            actions.go_back(1);
+        }
+    }
+    return result;
+}
+
+function randomSleep() {
+    let min = Math.ceil(3);
+    let max = Math.floor(6);
+    let time = (Math.floor(Math.random() * (max - min + 1)) + min) * 1000;
+    sleep(time);
+}
+
+// 是否首页
+
+
+// logd(searchKeywords);
+// if (searchKeywords) {
+
+// } else {
+//     logd('缺少关键词数据');
+//     actions.exceptionLog('缺少关键词数据');
+// }
+
+
+/**
+ * description 执行搜索
+ * @需要搜索多个关键词
+ * @return {boolean} : 返回是否成功
+ */
+function search() {
+    // let result = false;
+
+    // if (searchKeywords.length > 0) {
+    let keyword = searchKeywords[0];
+    // logd('准备执行搜索关键词:' + keyword);
+    dynamicData.deviceLog(false, {log_detail: '准备执行搜索关键词:' + keyword});
+    let hasSearch = actions.searchByKeyword(keyword, 4); //通过schema打开openActivity界面,搜索关键词
+    if (hasSearch) {
+        result = true;
+    }
+    // }
+
+    // return result;
+
+    // while (!thread.isCancelled(taskId)){
+    //     ecSdk.初始化节点();
+    //     //输入框
+    //     // if(ecSdk.findNode("text","广告",24,true,true)) {
+    //     // let searchLayout = id("com.baidu.searchbox:id/obfuscated").getOneNodeInfo(5000);
+    //     // if(searchLayout){
+    //
+    //     let searchBox = id('com.baidu.searchbox:id/baidu_searchbox').depth(15).getOneNodeInfo(5000)|| clz("android.widget.EditText").pkg("com.baidu.searchbox").getOneNodeInfo(5000)//;('com.baidu.searchbox:id/baidu_searchbox').
+    //     let editableTextSelector = clz("android.widget.EditText").pkg("com.baidu.searchbox").depth(10).getOneNodeInfo(5000);
+    //     // ecSdk.findNode("text","广告",24,true,true) ecSdk.找图('广告.png',true,)||
+    //     if(ecSdk.findNode("text","广告",24,false,true)){
+    //         ecSdk.findNode("text","广告",24,true,true)
+    //         ecSdk.msg('广告找到并点击')
+    //         sleep(4000);
+    //         swipeToPoint(121, 1769, 121, 369, 1000);
+    //         sleep(2000);
+    //         for (let i = 0; i < 2; i++) {
+    //             back();
+    //             ecSdk.msg("返回", 1000);
+    //         }
+    //     }
+    //         // else if(editableTextSelector){
+    //         // 	editableTextSelector.pasteText('辣条')
+    //         // 	ecSdk.msg('写入搜索值')
+    //         // }  else if(ecSdk.findNode("text","搜索",9,true,true)){
+    //         // 	ecSdk.msg('搜索按钮找到并点击')
+    //         // 	sleep(2000);
+    //     // }
+    //     else if(searchBox||editableTextSelector||ecSdk.findNode("text","搜索",9,false,true)){
+    //         ecSdk.msg('搜索按钮')
+    //         if(searchBox){
+    //             searchBox.click()
+    //         }
+    //         if(editableTextSelector){
+    //             let keyWords = searchKeywords[关键词执行位]
+    //             if(关键词执行位<searchKeywords.length){
+    //                 关键词执行位++
+    //             }else {
+    //                 关键词执行位 = 0
+    //             }
+    //             editableTextSelector.pasteText(keyWords)
+    //             ecSdk.msg('写入搜索值')
+    //             sleep(1000);
+    //
+    //         }
+    //         if(ecSdk.findNode("text","搜索",9,true,true)){
+    //             ecSdk.msg('点击')
+    //
+    //         }
+    //         sleep(2000);
+    //     // }else{
+    //         //退出
+    //         // exit()
+    //     }
+    //     // }else{
+    //     //     ecSdk.msg('没有找到搜索框')
+    //     // }
+    //
+    //     ecSdk.msg('程序执行中')
+    // }
+}
+
+
+/**
+ * description 浏览作品
+ * @ 商家作品
+ */
+function browseVideo1() {
+    let startNum = 0; //尝试抓取作品次数
+    let completedNumber = 0; //累计采集数
+    let reSearch = 0; //重新搜索次数
+    while (!thread.isCancelled(taskId)) {
+        logd('测试执行,app 操作 ');
+        if (completedNumber < total || completedNumber) {
+            try {
+                // begin common result page
+                sleep(random(1000, 4000));
+                //广告点击
+                let nodeData;
+                nodeData = clz("android.widget.TextView").text("广告").getOneNodeInfo(10 * 1000)
+                if (nodeData.click()) {
+                    //begin ad detail
+                    // nodeData.click()
+                    // sleep(random(1000,3000))
+                    // let i = 5; //swiper 5 times
+                    // while(i>0){
+                    sleep(random(1000, 5000))
+                    let swipered = swiper.pageSwiper();
+                    if (swipered) {
+                        let nodeDat = clz("android.widget.TextView").text("广告").getOneNodeInfo(10 * 1000);
+                        if (nodeDat.click()) {
+                            sleep(random(1000, 3000))
+                        }
+                    }
+                    // i--
+                    // }
+
+
+                    // sleep(random(1000,3000))
+                    // 返回 首页
+                    // let desc ;
+                    // desc = desc("首页").getOneNodeInfo(5000)
+                    // desc.click()
+                    // end ad detail
+                } else {
+                    throw 'err 跳出'
+                }
+                // end  common result page
+
+
+                logd('查看信息');
+
+                completedNumber++;
+            } catch (e) {
+                // if (!thread.isCancelled(taskId)) {
+                //
+                //         search();
+                //
+                //     startNum++;
+                // }
+                search();
+                completedNumber++
+            }
+        } else {
+            searchKeywords.shift();
+            search();
+        }
+
+    }
+}
+
+function browseVideo() {//处理搜索之后逻辑
+    let startNum = 0;//尝试抓取作品次数
+    let completedNumber = 0;//累计采集数
+    let reSearch = 0;//重新搜索次数
+    while (!thread.isCancelled(taskId)) {
+        logd('测试百度搜索 标签');
+        sleep(random(1000, 4000))
+        let nodeData;
+        nodeData = text("广告").pkg('com.baidu.searchbox').getOneNodeInfo(3 * 1000) || clz('com.baidu.webkit.sdk.WebView').pkg('com.baidu.searchbox').getOneNodeInfo(3 * 1000);
+        if (nodeData && nodeData.click()) {
+            try {
+                sleep(random(1000, 4000));
+                let swipered = swiper.pageSwiper();
+                if (swipered) {
+                    sleep(random(1000, 4000));
+                    // 返回 首页
+                    let desc;
+                    // clz('android.widget.ImageView').
+                    desc = desc("首页").getOneNodeInfo(5000)
+                    // desc = id('com.baidu.searchbox:id/obfuscated').getOneNodeInfo(6000)
+                    // com.baidu.searchbox:id/obfuscated
+                    if (desc && desc.click()) {
+                        sleep(random(1000, 3000));
+                        //找下一个
+                        if (reSearch > 1 && searchKeywords.length > 0) {
+                            searchKeywords.shift();
+                            search()
+                        }
+                        reSearch++
+                    }
+                    // end ad detail
+                }
+            } catch (e) {
+                //错误重新执行
+                if (!thread.isCancelled(taskId)) {
+                    logd('未找到有效广告:' + e);
+                    if (startNum < 3) {
+                        logd('未找到广告信息,尝试翻页');
+                        actions.videoSwiper(null);
+                    } else {
+                        logd('未找到广告信息,重新搜索');
+                        //找下一个
+                        if (reSearch > 1 && searchKeywords.length > 0) {
+                            searchKeywords.shift();
+                            search()
+                        }
+                        reSearch++
+                    }
+                    startNum++
+                }
+            }
+        } else {
+            if (startNum < 10) {
+                logd('未找到作品信息,尝试翻页');
+                actions.videoSwiper(null);
+            } else {
+                logd('未找到作品信息,重新搜索');
+                //找下一个
+                if (reSearch > 1 && searchKeywords.length > 0) {
+                    searchKeywords.shift();
+                    search()
+                }
+                reSearch++
+            }
+            startNum++
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/rpa/src/slib/actions.js b/rpa/src/slib/actions.js
index 12f787f..6882f24 100644
--- a/rpa/src/slib/actions.js
+++ b/rpa/src/slib/actions.js
@@ -132,6 +132,62 @@
  * @param platform {number} : 平台 (百度,QQ浏览器)
  * @return {boolean} : 返回是否成功
  */
+// actions.prototype.searchByKeyword = function (keyword, platform) {
+//     let result = false;
+//     let startNum = 3; //重试次数
+//     //搜索内容
+//     logd('准备搜索');
+//     dynamicData.deviceLog(true, {log_detail: '准备搜索'});
+//     let schemesItem = datas.schemes.filter(item => item.platform === platform);
+//     let map = {"uri": schemesItem[0].scheme["search"] + keyword};
+//     let num = 0;
+//     search:
+//         while (num < startNum) {
+//             try {
+//                 let searchLayout = id("com.baidu.searchbox:id/obfuscated").getOneNodeInfo(5000);
+//                 if (searchLayout) {
+//                     let editableTextSelector = clz("android.widget.EditText");
+//                     if (editableTextSelector) {
+//                         click(editableTextSelector);
+//                         logd('搜索框已点击');
+//                         editableTextSelector.clearText();
+//                         inputText(editableTextSelector, keyword);
+//                         logd('输入搜索内容:' + keyword);
+//                         let searchBtn = text("搜索").getOneNodeInfo(5000);
+//                         if (searchBtn.click()) {
+//                             logd('搜索已点击');
+//                             result = true;
+//                         }
+//                     } else {
+//                         let searchBox = searchLayout.getOneNodeInfo(clz("android.widget.TextView"), 5000);
+//                         if (searchBox.click()) {
+//                             logd('搜索框已点击');
+//                             let searchLayout2 = id("com.baidu.searchbox:id/obfuscated").getOneNodeInfo(5000);
+//                             if (searchLayout2) {
+//                                 let editTextSelector2 = searchLayout2.getOneNodeInfo(clz("android.widget.EditText"), 5000);
+//                                 editTextSelector2.clearText();
+//                                 inputText(editTextSelector2, keyword);
+//                                 logd('输入搜索内容:' + keyword);
+//                                 let searchBtn = text("搜索").getOneNodeInfo(5000);
+//                                 if (searchBtn.click()) {
+//                                     logd('搜索已点击');
+//                                     result = true;
+//                                 }
+//                             }
+//                         }
+//                     }
+
+//                 }
+//                 logd(JSON.stringify(map));
+//             } catch (e) {
+//                 logd('搜索失败:' + e);
+//                 actions.prototype.exceptionLog('搜索失败:' + e);
+//             }
+//             num++;
+//         }
+//     return result;
+// }
+
 actions.prototype.searchByKeyword = function (keyword, platform) {
     let result = false;
     let startNum = 3; //重试次数
@@ -141,32 +197,25 @@
     let schemesItem = datas.schemes.filter(item => item.platform === platform);
     let map = {"uri": schemesItem[0].scheme["search"] + keyword};
     let num = 0;
+
     search:
         while (num < startNum) {
             try {
-                let searchLayout = id("com.baidu.searchbox:id/obfuscated").getOneNodeInfo(5000);
-                if (searchLayout) {
-                    let editableTextSelector = clz("android.widget.EditText");
-                    if (editableTextSelector) {
-                        click(editableTextSelector);
+                if (platform === 4) {
+
+                    let searchLayout = id("com.baidu.searchbox:id/baidu_searchbox").getOneNodeInfo(5000);
+                    if (searchLayout&&searchLayout.click()) {
+                        // if (editableTextSelector) {
+                        // click(editableTextSelector);
+                        // editableTextSelector.click()
+                        let editableTextSelector = clz("android.widget.EditText").pkg("com.baidu.searchbox").getOneNodeInfo(5000);
                         logd('搜索框已点击');
-                        editableTextSelector.clearText();
-                        inputText(editableTextSelector, keyword);
-                        logd('输入搜索内容:' + keyword);
-                        let searchBtn = text("搜索").getOneNodeInfo(5000);
-                        if (searchBtn.click()) {
-                            logd('搜索已点击');
-                            result = true;
-                        }
-                    } else {
-                        let searchBox = searchLayout.getOneNodeInfo(clz("android.widget.TextView"), 5000);
-                        if (searchBox.click()) {
-                            logd('搜索框已点击');
-                            let searchLayout2 = id("com.baidu.searchbox:id/obfuscated").getOneNodeInfo(5000);
-                            if (searchLayout2) {
-                                let editTextSelector2 = searchLayout2.getOneNodeInfo(clz("android.widget.EditText"), 5000);
-                                editTextSelector2.clearText();
-                                inputText(editTextSelector2, keyword);
+                        sleep(1000);
+                        // editableTextSelector.clearText();
+                        if (editableTextSelector) {
+                            // editableTextSelector.pasteText(keyword)
+                            if (imeInputText(null, keyword)) {
+
                                 logd('输入搜索内容:' + keyword);
                                 let searchBtn = text("搜索").getOneNodeInfo(5000);
                                 if (searchBtn.click()) {
@@ -175,9 +224,90 @@
                                 }
                             }
                         }
-                    }
 
+                    } else {
+                        logd('没有找找到搜索按钮,第二种搜索方式');
+                        let searchLayout2 = id("com.baidu.searchbox:id/obfuscated").depth(14).drawingOrder(2).child().getOneNodeInfo(5000)
+                        // toast(JSON.stringify(searchLayout2));
+                        if (searchLayout2&&searchLayout2.click()) {
+                            let editableTextSelector = clz("android.widget.EditText").pkg("com.baidu.searchbox").getOneNodeInfo(5000);
+                            sleep(1000);
+                            if (editableTextSelector) {
+                                if (imeInputText(null, keyword)) {
+
+                                    let searchBtn = text("搜索").getOneNodeInfo(5000);
+                                    if (searchBtn.click()) {
+                                        logd('搜索已点击');
+                                        result = true;
+                                    }
+                                }
+                            }
+                        }else{
+                            logd('没有找到搜索按钮 ');
+                        }
+                    }
+                    // editableTextSelector.clearText();
+                    // imeInputText(editableTextSelector, keyword);
+                    // searchBtn.click();
+                    // }
+                    // } else {
+                    //     let searchBox = searchLayout.getOneNodeInfo(clz("android.widget.TextView"), 5000);
+                    //     if (searchBox.click()) {
+                    //         logd('搜索框已点击');
+                    //         let searchLayout2 = id("com.baidu.searchbox:id/obfuscated").getOneNodeInfo(5000);
+                    //         if (searchLayout2) {
+                    //             let editTextSelector2 = searchLayout2.getOneNodeInfo(clz("android.widget.EditText"), 5000);
+                    //             editTextSelector2.clearText();
+                    //             inputText(editTextSelector2, keyword);
+                    //             logd('输入搜索内容:' + keyword);
+                    //             let searchBtn = text("搜索").getOneNodeInfo(5000);
+                    //             if (searchBtn.click()) {
+                    //                 logd('搜索已点击');
+                    //                 result = true;
+                    //             }
+                    //         }
+                    //     }
+                } else if (platform === 5) {
+                    //qq浏览器
+                    let searchLayout = id("com.tencent.mtt:id/search_bar_tv_hotword").getOneNodeInfo(5000);
+                    if (searchLayout && searchLayout.click()) {
+                        //获取到 准备写入
+                        sleep(1000);
+                        let editableTextSelector = id("com.tencent.mtt:id/search_frame_input_bar").getOneNodeInfo(5000);//.pkg('com.tencent.mtt')
+                        // let editableTextSelector = clz("android.widget.EditText").pkg("com.tencent.mtt").getOneNodeInfo(3000)
+                        logd(JSON.stringify(editableTextSelector));
+                        sleep(1000);
+                        if (editableTextSelector) {
+                            // imeInputText(editableTextSelector,keyword)
+                            // imeInputText(null,keyword)
+                            if (imeInputText(null, keyword)) {
+                                sleep(3000);
+                                let searchBtn = text("搜索").getOneNodeInfo(5000);
+                                if (searchBtn.click()) {
+                                    logd('搜索已点击');
+                                    result = true;
+                                }
+                            } else {
+                                logd('imeInputText 失败');
+
+                            }
+
+                            // pasteText(editableTextSelector, keyword)
+                            // editableTextSelector.inputText(keyword)
+
+                            // if (searchBtn.click()) {
+                            //     logd('搜索已点击');
+                            //     result = true;
+                            // }
+                        } else {
+                            logd('没有找到要写入的搜索框');
+                        }
+                    } else {
+                        logd('没有找到搜索框')
+                    }
                 }
+
+                // }
                 logd(JSON.stringify(map));
             } catch (e) {
                 logd('搜索失败:' + e);

--
Gitblit v1.9.3