bugly中批量隐藏版本

简介: App项目使用Bugly的内测分发功能进行整包的测试,但日积月累之后,版本就会特别多。而线上同时跑的版本可能不过三个左右,那么多版本会干扰到查看崩溃、选择版本,如何隐藏呢? 右上角,更多 –> 产品设置 –> 版本管理 可是bugly有bug,只能设置显示,不能设置隐藏 >_>> 24 & ...

App项目使用Bugly的内测分发功能进行整包的测试,但日积月累之后,版本就会特别多。而线上同时跑的版本可能不过三个左右,那么多版本会干扰到查看崩溃、选择版本,如何隐藏呢?

右上角,更多 –> 产品设置 –> 版本管理

image

可是bugly有bug,只能设置显示,不能设置隐藏 >_<,好几十个版本要一个一个去点击隐藏,好麻烦,所以就用Fiddler抓了一下包然后整了一个脚本。

注:本想直接用一个for循环里 new Image().src 的方法直接批量请求,但发现它请求的时候参数fsn每次都不一样,而且头里面加了X-token,所以只能去扒它的代码了,用webpack发布的脚本,对源码进行了适当的修改。

//     uuid.js
//
//     Copyright (c) 2010-2012 Robert Kieffer
//     MIT License - http://opensource.org/licenses/mit-license.php
// Unique ID creation requires a high quality random # generator.  We feature
// detect to determine the best RNG source, normalizing to a function that
// returns 128-bits of randomness, since that's what's usually required
// var _rng = require('./rng');
var rng;

// var crypto = global.crypto || global.msCrypto; // for IE 11
// if (crypto && crypto.getRandomValues) {
//   // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
//   // Moderately fast, high quality
//   var _rnds8 = new Uint8Array(16);
//   rng = function whatwgRNG() {
//     crypto.getRandomValues(_rnds8);
//     return _rnds8;
//   };
// }

if (!rng) {
    // Math.random()-based (RNG)
    //
    // If all else fails, use Math.random().  It's fast, but is of unspecified
    // quality.
    var _rnds = new Array(16);
    rng = function() {
        for (var i = 0, r; i < 16; i++) {
            if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
            _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
        }

        return _rnds;
    };
}

// module.exports = rng;
var _rng = rng;



//////////////////
// WEBPACK FOOTER
// ./~/uuid/rng-browser.js
// module id = 955
// module chunks = 0


// Maps for number <-> hex string conversion
var _byteToHex = [];
var _hexToByte = {};
for (var i = 0; i < 256; i++) {
    _byteToHex[i] = (i + 0x100).toString(16).substr(1);
    _hexToByte[_byteToHex[i]] = i;
}

// **`parse()` - Parse a UUID into it's component bytes**
function parse(s, buf, offset) {
    var i = (buf && offset) || 0,
        ii = 0;

    buf = buf || [];
    s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
        if (ii < 16) { // Don't overflow!
            buf[i + ii++] = _hexToByte[oct];
        }
    });

    // Zero out remaining bytes if string was short
    while (ii < 16) {
        buf[i + ii++] = 0;
    }

    return buf;
}

// **`unparse()` - Convert UUID byte array (ala parse()) into a string**
function unparse(buf, offset) {
    var i = offset || 0,
        bth = _byteToHex;
    return bth[buf[i++]] + bth[buf[i++]] +
        bth[buf[i++]] + bth[buf[i++]] + '-' +
        bth[buf[i++]] + bth[buf[i++]] + '-' +
        bth[buf[i++]] + bth[buf[i++]] + '-' +
        bth[buf[i++]] + bth[buf[i++]] + '-' +
        bth[buf[i++]] + bth[buf[i++]] +
        bth[buf[i++]] + bth[buf[i++]] +
        bth[buf[i++]] + bth[buf[i++]];
}

// **`v1()` - Generate time-based UUID**
//
// Inspired by https://github.com/LiosK/UUID.js
// and http://docs.python.org/library/uuid.html

// random #'s we need to init node and clockseq
var _seedBytes = _rng();

// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
var _nodeId = [
    _seedBytes[0] | 0x01,
    _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
];

// Per 4.2.2, randomize (14 bit) clockseq
var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;

// Previous uuid creation time
var _lastMSecs = 0,
    _lastNSecs = 0;

// See https://github.com/broofa/node-uuid for API details
function v1(options, buf, offset) {
    var i = buf && offset || 0;
    var b = buf || [];

    options = options || {};

    var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;

    // UUID timestamps are 100 nano-second units since the Gregorian epoch,
    // (1582-10-15 00:00).  JSNumbers aren't precise enough for this, so
    // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
    // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
    var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();

    // Per 4.2.1.2, use count of uuid's generated during the current clock
    // cycle to simulate higher resolution clock
    var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;

    // Time since last uuid creation (in msecs)
    var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000;

    // Per 4.2.1.2, Bump clockseq on clock regression
    if (dt < 0 && options.clockseq === undefined) {
        clockseq = clockseq + 1 & 0x3fff;
    }

    // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
    // time interval
    if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
        nsecs = 0;
    }

    // Per 4.2.1.2 Throw error if too many uuids are requested
    if (nsecs >= 10000) {
        throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
    }

    _lastMSecs = msecs;
    _lastNSecs = nsecs;
    _clockseq = clockseq;

    // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
    msecs += 12219292800000;

    // `time_low`
    var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
    b[i++] = tl >>> 24 & 0xff;
    b[i++] = tl >>> 16 & 0xff;
    b[i++] = tl >>> 8 & 0xff;
    b[i++] = tl & 0xff;

    // `time_mid`
    var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
    b[i++] = tmh >>> 8 & 0xff;
    b[i++] = tmh & 0xff;

    // `time_high_and_version`
    b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
    b[i++] = tmh >>> 16 & 0xff;

    // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
    b[i++] = clockseq >>> 8 | 0x80;

    // `clock_seq_low`
    b[i++] = clockseq & 0xff;

    // `node`
    var node = options.node || _nodeId;
    for (var n = 0; n < 6; n++) {
        b[i + n] = node[n];
    }

    return buf ? buf : unparse(b);
}

// **`v4()` - Generate random UUID**

// See https://github.com/broofa/node-uuid for API details
function v4(options, buf, offset) {
    // Deprecated - 'format' argument, as supported in v1.2
    var i = buf && offset || 0;

    if (typeof(options) == 'string') {
        buf = options == 'binary' ? new Array(16) : null;
        options = null;
    }
    options = options || {};

    var rnds = options.random || (options.rng || _rng)();

    // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
    rnds[6] = (rnds[6] & 0x0f) | 0x40;
    rnds[8] = (rnds[8] & 0x3f) | 0x80;

    // Copy bytes to buffer, if provided
    if (buf) {
        for (var ii = 0; ii < 16; ii++) {
            buf[i + ii] = rnds[ii];
        }
    }

    return buf || unparse(rnds);
}

// Export public API
var uuid = v4;
uuid.v1 = v1;
uuid.v4 = v4;
uuid.parse = parse;
uuid.unparse = unparse;



function $param(json) {
    return Object.keys(json).reduce((sum, key) => {
        return `${(sum.length ? `${sum}&` : sum)}${key}=${json[key]}`;
    }, '');
}




function getCookie(name) {
    var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
    if (arr = document.cookie.match(reg))
        return unescape(arr[2]);
    else
        return null;
}

// 根据php打到cookie的skey值生成一个token,然后把这个值塞到请求头
function getToken() {
    /* eslint-disable no-bitwise*/
    var skey = getCookie('token-skey');
    let hash = 5381;
    if (skey) {
        for (let i = 0, len = skey.length; i < len; ++i) {
            hash += (hash << 5 & 0x7fffffff) + skey.charAt(i).charCodeAt();
        }
        return hash & 0x7fffffff;
    }
}

var APP_JSON_CONTENT_TYPE = 'application/json;charset=utf-8';
var REG_URL_PARAM = /\{(\w+)\}/ig;
var basehttp = "https://bugly.qq.com/v2";
var baseUrl = "https://bugly.qq.com/v2";

var apiBaseUrl = "https://api.bugly.qq.com/bugly2.0/";

function ajaxAsync(method, uri, data = {}, options) {
    var headers = {
        method,
        credentials: 'include',
        headers: {
            'Content-Type': APP_JSON_CONTENT_TYPE,
            Accept: APP_JSON_CONTENT_TYPE,
            'X-token': getToken(),
        },
    };

    // var mmHeader = {
    //   method: 'POST',
    //   credentials: 'include',
    //   headers: {
    //     'Content-Type': APP_JSON_CONTENT_TYPE,
    //     Accept: APP_JSON_CONTENT_TYPE,
    //     'X-token': getToken(),
    //   },
    // };

    if (method !== 'GET' && method !== 'DELETE') {
        headers.body = JSON.stringify(data);
    }

    // console.log("fetch header: %s, url: %s", JSON.stringify(headers));
    var startTime = new Date().getTime();
    return fetch(getReqUrl(method, uri, data, options), headers).then((response) => {
            var endTime = new Date().getTime();
            if (uri.indexOf('mmLogFromWeb') === -1 && uri.indexOf('trail/organize') === -1 &&
                uri.indexOf('trail/trailer') === -1) {
                // var param = {
                //   interface: uri,
                //   times: endTime - startTime,
                //   statusCode: response.status,
                // };
                // mmHeader.body = JSON.stringify(param);
                var imgSrc = `${window.webSitePathName}/mmLogFromWeb?interface=${uri}&times=${endTime - startTime}&statusCode=${response.status}`;
                var img = new Image();
                img.src = imgSrc;
                // fetch(getReqUrl('POST', "/mmLogFromWeb", data, {}), mmHeader).catch((response) => Promise.resolve({ response }));
            }
            return new Promise((resolve) => {
                response.json().then((json) => {
                    resolve({
                        json,
                        response
                    });
                }).catch(() => {
                    resolve({
                        response
                    });
                });
            });
        }).then(({
            json,
            response
        }) => Promise.resolve({
            json,
            response
        }))
        .catch((response) => {
            return Promise.resolve({
                response
            });
        });
}

function getReqUrl(method, uri, data, options) {
    let url = uri;
    // console.log("getReqUrl: ", method, ", data: ", data);
    if (uri.slice(0, basehttp.length) === basehttp) {
        url = uri;
        // url = uri;
    } else {
        url = (uri.indexOf('api') > -1 ? apiBaseUrl : baseUrl) + uri;
    }
    // console.log("before data: ", JSON.stringify(data), ", url: ", url, ", userId: ", data.userId);
    url = url.replace(REG_URL_PARAM, (...args) => {
        var key = args && !!args[1] && args[1];
        var ret = key ? data[key] : args[0];
        delete data[key];
        return ret;
    });
    //if (!Object.keys(data).length) {
    //  return url;
    //}
    if (method === 'GET' || method === 'DELETE') {
        var dataLength = Object.keys(data).length;
        url += (dataLength > 0 ? `?${$param(data)}` : '');
    }
    if (options && options.query) {
        url += (`?${$param(options.query)}`);
    }
    // 万一不是restful设计的url,需要处理,先?分割,然后token分割,然后合并
    // console.log("url === ", url);
    // if (url && url.indexOf("?") != -1) {
    //    let urlArr = url.split("?");//
    //    if (urlArr.length) {
    //        let baseUrlHead = urlArr[0].split("/token/")[0];
    //        url = baseUrlHead + "?" + urlArr[1] + "&token=" + token;//重新拼接url
    //    }
    // }
    if (url) {
        if (url.indexOf('?') !== -1) {
            url += '&fsn=' + uuid.v4();
        } else {
            url += '?fsn=' + uuid.v4();
        }
    }

    return url;
}

// *********** 删除版本调用此方法即可 ***********
function deleteVersion(versionName) {
    var uri = '/apps/{appId}/platformId/{pid}/userId/{userId}/isShow/{isShow}/disableVersion';
    var data = {
        appId: "当前操作的AppID",
        pid: 1,
        isShow: false,
        userId: "当前登录的QQ号(需要有操作权限)",
        version: ""
    };

    data.version = versionName;

    return ajaxAsync('GET', uri, data, undefined);
}


// 示例  V2.4.xxx.xx的版本仅保留三个指定的版本,其余全部删除掉
var liElementArray = document.getElementsByTagName('li');
var keepVersionList = ['2.4.2.2003206', '2.4.0.2003171', '2.4.3.2003255', '2.4.1.2003203'];
for (var i = 0, len = liElementArray.length; i < len; i++) {
    var liElement = liElementArray[i];
    if (liElement.className === '_2DfZ7MzVviIlRz6RqqrP3w') {
        if (/\>(2\.4\.\d\.\d+)\</.test(liElement.outerHTML)) {
            var versionName = RegExp['$1'];

            if (keepVersionList.indexOf(versionName) == -1) {
                console.log(versionName);

                deleteVersion(versionName);
            } else {
                console.log('skip version : ' + versionName);
            }

        }
    }
}

 

如果正巧遇到有几十个上百个版本需要批量删除的,可以直接copy上面的代码,适当进行修改便可使用。

目录
相关文章
|
4月前
|
Serverless 文件存储 对象存储
函数计算操作报错合集之如何处理安装了ControlNet但无法加载出预览图并报错
在使用函数计算服务(如阿里云函数计算)时,用户可能会遇到多种错误场景。以下是一些常见的操作报错及其可能的原因和解决方法,包括但不限于:1. 函数部署失败、2. 函数执行超时、3. 资源不足错误、4. 权限与访问错误、5. 依赖问题、6. 网络配置错误、7. 触发器配置错误、8. 日志与监控问题。
函数计算操作报错合集之如何处理安装了ControlNet但无法加载出预览图并报错
|
6月前
|
IDE 开发工具 Android开发
【更新公告】AirtestIDE更新至1.2.17版本
【更新公告】AirtestIDE更新至1.2.17版本
【更新公告】AirtestIDE更新至1.2.17版本
|
6月前
|
前端开发 开发工具 iOS开发
mPaaS常见问题之真机预览与调试扫码调式 release包开启日志如何解决
mPaaS(移动平台即服务,Mobile Platform as a Service)是阿里巴巴集团提供的一套移动开发解决方案,它包含了一系列移动开发、测试、监控和运营的工具和服务。以下是mPaaS常见问题的汇总,旨在帮助开发者和企业用户解决在使用mPaaS产品过程中遇到的各种挑战
124 0
|
6月前
|
IDE 开发工具 Android开发
【更新公告】AirtestIDE更新至1.2.16版本
【更新公告】AirtestIDE更新至1.2.16版本
|
6月前
|
IDE 开发工具 开发者
【更新公告】AirtestIDE更新至1.2.15版本
【更新公告】AirtestIDE更新至1.2.15版本
117 0
|
6月前
|
Java 定位技术 语音技术
如何处理 Xcode 上传 IPA 文件后无法在后台架构版本中显示的问题?
如何处理 Xcode 上传 IPA 文件后无法在后台架构版本中显示的问题?
|
开发工具 开发者 iOS开发
​ 如何处理Xcode上传IPA文件后无法在后台架构版本中显示的问题?
1、ipa包符合要求,显示正在处理,这种就是成功上传,等待处理即可(根据包大小,一般处理几分钟到一两个钟都有可能) 2、ipa不符合要求,没有出现正在处理,或者刷新页面正在处理的版本消失 出现这种情况说明你上传的这个ipa包有问题,苹果会发送具体原因到邮箱(开发者账号就是邮箱地址,) 登录邮箱去查看反馈邮件(反馈邮件也可能在垃圾箱),修改错误重新再打包上传。
​  如何处理Xcode上传IPA文件后无法在后台架构版本中显示的问题?
|
Java 定位技术 语音技术
如何处理 Xcode 上传 IPA 文件后无法在后台架构版本中显示的问题?
AU 上传 ipa 出现下图红框提示说明成功上传,但有时 App Store 后台没有出现构建版本,请查看下面详细说明!
|
Java 定位技术 语音技术
​如何处理Xcode上传IPA文件后无法在后台架构版本中显示的问题?
​如何处理Xcode上传IPA文件后无法在后台架构版本中显示的问题?
Revit二次开发—更改激活视图(activeview)失败原因
Revit二次开发—更改激活视图(activeview)失败原因
Revit二次开发—更改激活视图(activeview)失败原因