Greasy Fork is available in English.

DDNSTO自动续期

路由器插件ddnsto免费套餐续期。

// ==UserScript==
// @name         DDNSTO自动续期
// @icon         https://www.ddnsto.com/app/favicon.ico
// @namespace    http://www.ddnsto.com/
// @version      0.1.4
// @description  路由器插件ddnsto免费套餐续期。
// @author       everstu
// @match        *www.ddnsto.com/*
// @require      https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js
// @require      https://cdn.bootcdn.net/ajax/libs/layer/3.1.1/layer.min.js
// @grant        unsafeWindow
// @license      MIT
// @run-at       document-body
// ==/UserScript==

(function () {
        'use strict';
        $('head').append('<link href="https://cdn.bootcdn.net/ajax/libs/layer/3.1.1/theme/default/layer.min.css" rel="stylesheet">')

        setTimeout(function () {
            let myHref = window.location.href;
            let product, userprd, requestUrl, requestData, is_async, method;
            let nowTime = Date.now();
            let daySeconds = 24 * 60 * 60 * 1000;//每天有多少毫秒
            let feeDiff = parseInt(getStorge('feeDiff'));
            let tipsLable = '.user';
            $('#app').append('<div style="position: fixed;z-index:9999;top:5px;right:1px;background: #000000;color: white;border-radius: 17px;width: 70px;height: 20px;text-align:center;line-height: 20px;font-size:12px;opacity: 0.6;cursor:pointer;" id="plugin_setting">插件设置</div>');
            $('#plugin_setting').click(() => {
                showFeeDiff();
            });

            if (myHref.indexOf('app/#/devices') !== -1) {
                console.log(feeDiff);
                if (!feeDiff) {
                    showFeeDiff();
                    return;
                }
                //获取用户产品列表
                requestUrl = 'https://www.ddnsto.com/api/user/routers/?limit=50&offset=0';
                is_async = false;
                doRequest(function (res) {
                    if (res.hasOwnProperty('results')) {
                        let tempUserprd = res.results;
                        let i = 0;
                        userprd = {};
                        $.each(tempUserprd, (k, v) => {
                            if (v.hasOwnProperty('has_active_non_free_plan') && v.has_active_non_free_plan === false) {
                                userprd[i] = v;
                                i++;
                            }
                        });
                    }
                });

                let is_return = false;
                if (empty(userprd)) {
                    layerTips('用户免费套餐为空,不执行续期操作。');
                    is_return = true;
                } else if (userprd.length > 1) {
                    layerTips('用户有两个免费套餐,不执行续期操作。');
                    is_return = true;
                }

                let userPrdinfo = userprd[0];
                if (empty(userPrdinfo)) {
                    layerTips('用户免费套餐为空,不执行续期操作。');
                    is_return = true;
                } else {
                    let exprieTime = Date.parse(userPrdinfo.active_plan.product_expired_at);
                    let diffTime = exprieTime - nowTime;
                    if (diffTime > feeDiff * daySeconds) {
                        let diffDays = (diffTime / daySeconds).toFixed(1);
                        layerTips('当前免费套餐有效期:' + diffDays + '天。<br>套餐有效期大于' + feeDiff + '天,停止自动续期。');
                        is_return = true;
                    }
                }

                if (is_return) {
                    return;
                }

                //获取可用免费套餐
                requestUrl = 'https://www.ddnsto.com/api/user/product/products/';
                is_async = false;
                doRequest(function (res) {
                    $.each(res, (k, v) => {
                        if (v.hasOwnProperty('price') && parseInt(v.price) === 0) {
                            product = v;
                        }
                    });
                });

                //执行下单操作。
                requestUrl = 'https://www.ddnsto.com/api/user/product/orders/';
                requestData = {product_id: product.id, uuid_from_client: generateUUID()};
                method = 'post';
                doRequest(function (res) {
                    let order_id = res.id;
                    //查询订单,并不知道有什么用。
                    requestUrl = 'https://www.ddnsto.com/api/user/product/orders/' + order_id + '/';
                    doRequest(function (res) {
                        requestUrl = userprd[0].url;
                        requestData = {plan_ids_to_add: [order_id], server: userprd[0].server};
                        method = 'patch';
                        doRequest(function (res) {
                            let expireDate = res.longest_plan_due_date;
                            let routerName = res.name;
                            layerTips('续期成功,路由器 ' + routerName + '<br>当前到期时间:' + utc2beijing(expireDate));
                        });
                    });
                });
            }

            function doRequest(callback) {
                if (is_async === undefined) {
                    is_async = true;
                } else {
                    is_async = is_async === true;
                }
                if (!method) {
                    method = 'get';
                }
                if (!requestUrl) {
                    alert('未设置url,请检查代码');
                    return;
                }
                let opt = {
                    type: method.toUpperCase(),
                    async: is_async,
                    url: requestUrl,
                    contentType: "application/json;charset=UTF-8", //必须这样写
                    dataType: "json",
                    beforeSend: function (request) {
                        request.setRequestHeader("x-csrftoken", getCsrfToken());
                    },
                    success: function (res) {
                        callback(res);
                    },
                };

                if (requestData !== undefined && requestData) {

                    opt.data = JSON.stringify(requestData);
                }

                method = requestUrl = is_async = requestData = undefined;

                $.ajax(opt);
            }

            function generateUUID() {
                let d = new Date().getTime();
                if (window.performance && typeof window.performance.now === "function") {
                    d += performance.now(); //use high-precision timer if available
                }
                let uuid = 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                    let r = (d + Math.random() * 16) % 16 | 0;
                    d = Math.floor(d / 16);
                    return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
                });
                return uuid;
            }

            function getCsrfToken() {
                return getCookie('csrftoken');
            }

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

            function getStorge(name, type) {
                if (type === 'session') {
                    return sessionStorage.getItem(name);
                }
                return localStorage.getItem(name);
            }

            function setStorge(name, data, type) {
                if (type === 'session') {
                    return sessionStorage.setItem(name, data);
                }
                return localStorage.setItem(name, data);
            }

            function removeStorge(name, type) {
                if (type === 'session') {
                    return sessionStorage.removeItem(name);
                }
                return localStorage.removeItem(name)
            }

            function empty(value) {
                let ret = false;
                if (!value) {
                    ret = true;
                }

                return ret;
            }

            function utc2beijing(utc_datetime) {
                // 转为正常的时间格式 年-月-日 时:分:秒
                var T_pos = utc_datetime.indexOf('T');
                var Z_pos = utc_datetime.indexOf('Z');
                var year_month_day = utc_datetime.substr(0, T_pos);
                var hour_minute_second = utc_datetime.substr(T_pos + 1, Z_pos - T_pos - 1);
                var new_datetime = year_month_day + " " + hour_minute_second; // 2017-03-31 08:02:06

                // 处理成为时间戳
                var timestamp = new Date(Date.parse(new_datetime));
                timestamp = timestamp.getTime();
                timestamp = timestamp / 1000;

                // 增加8个小时,北京时间比utc时间多八个时区
                timestamp = timestamp + 8 * 60 * 60;

                var date = new Date(parseInt(timestamp) * 1000);
                var year = date.getFullYear();
                var mon = date.getMonth() + 1;
                var day = date.getDate();
                var hour = date.getHours();
                var min = date.getMinutes();

                // 时间戳转为时间
                return year + '年' + mon + '月' + day + '日 ' + hour + ':' + min;
            }

            /**
             * 弹出layer tips
             * @param msg
             */
            function layerTips(msg) {
                layer.tips(msg + '<br /><br />提示:当前设置套餐剩余 ' + feeDiff + ' 天时自动续费。', tipsLable, {tips: 1});
            }

            /**
             * 显示设置自动续期天数弹窗
             */
            function showFeeDiff() {
                let message = '请输入>=1且<=5的数字';
                feeDiff = feeDiff ? feeDiff : 5;
                layer.open({
                    title: '输入自动续期天数',
                    type: 0,
                    content: '请输入套餐剩余几天自动续期:<br /><input name="feeDiff" id="feeDiff" value="' + feeDiff + '" type="number" style="height: 20px;width: 70px;line-height: 20px;margin:10px 0;background: #f1f1f1">&nbsp;天<br />' + message,
                    yes: (index) => {
                        let tmpFeeDiff = $('#feeDiff').val();
                        if (tmpFeeDiff < 1 || tmpFeeDiff > 5) {
                            layer.tips(message, '#feeDiff', {tips: 1});
                            return;
                        }

                        feeDiff = tmpFeeDiff;
                        layer.close(index);
                        setStorge('feeDiff', feeDiff);
                        window.location.reload();
                    },
                    cancel: () => {
                        feeDiff = feeDiff ? feeDiff : 5;
                        setStorge('feeDiff', feeDiff);
                        window.location.reload();
                    },
                });
            }

        }, 2000);
    }
)();