├── .github └── workflows │ └── DockerHub.yml ├── .gitignore ├── docker ├── Dockerfile ├── docker-compose.yml ├── docker-entrypoint.sh └── onekey-docker.sh ├── export_sharecodes.sh ├── git_pull.sh ├── jd.sh ├── license ├── onekey-install.sh ├── panel ├── package.json ├── public │ ├── auth.html │ ├── crontab.html │ ├── css │ │ ├── codemirror.min.css │ │ ├── dracula.css │ │ ├── main.css │ │ ├── merge.css │ │ └── twilight.css │ ├── diff.html │ ├── home.html │ ├── js │ │ ├── codemirror.min.js │ │ ├── comment.js │ │ ├── diff_match_patch.js │ │ ├── javascript.js │ │ ├── jquery.min.js │ │ ├── merge.js │ │ ├── qrcode.min.js │ │ ├── shell.js │ │ ├── sublime.js │ │ └── sweetalert2.js │ ├── pwd.html │ ├── run.html │ └── tasklog.html └── server.js ├── readme.md ├── rm_log.sh ├── sample ├── auth.json ├── config.sh.sample └── crontab.list.sample ├── thanks.png └── update.js /.github/workflows/DockerHub.yml: -------------------------------------------------------------------------------- 1 | name: DockerHub 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - v3 7 | schedule: 8 | - cron: '0 21 * * *' 9 | 10 | jobs: 11 | docker: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Set up QEMU 18 | uses: docker/setup-qemu-action@v1 19 | 20 | - name: Set up Docker Buildx 21 | uses: docker/setup-buildx-action@v1 22 | 23 | - name: Login to DockerHub 24 | uses: docker/login-action@v1 25 | with: 26 | username: ${{ secrets.MY_ID }} 27 | password: ${{ secrets.MY_PASSWORD }} 28 | 29 | - name: Build & Push 30 | uses: docker/build-push-action@v2 31 | with: 32 | context: ./docker/ 33 | platforms: linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v6,linux/ppc64le,linux/s390x 34 | push: true 35 | tags: thisispatrick/jd-base:v3 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | panel/node_modules* 2 | panel/yarn.lock 3 | panel/package-lock.json 4 | scripts* 5 | log* 6 | config/* 7 | version 8 | new_task 9 | drop_task 10 | send_count -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine 2 | LABEL maintainer="RikudouPatrickstar" 3 | ARG JD_BASE_URL=https://github.com/RikudouPatrickstar/jd-base 4 | ARG JD_BASE_BRANCH=v3 5 | ARG JD_SCRIPTS_URL=https://github.com/RikudouPatrickstar/jd_scripts 6 | ARG JD_SCRIPTS_BRANCH=master 7 | ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ 8 | LANG=zh_CN.UTF-8 \ 9 | SHELL=/bin/bash \ 10 | PS1="\u@\h:\w \$ " \ 11 | JD_DIR=/jd 12 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \ 13 | && apk update -f \ 14 | && apk upgrade \ 15 | && apk --no-cache add -f bash \ 16 | coreutils \ 17 | moreutils \ 18 | git \ 19 | wget \ 20 | curl \ 21 | nano \ 22 | tzdata \ 23 | perl \ 24 | openssl \ 25 | && rm -rf /var/cache/apk/* \ 26 | && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ 27 | && echo "Asia/Shanghai" > /etc/timezone \ 28 | && git clone -b ${JD_BASE_BRANCH} ${JD_BASE_URL} ${JD_DIR} \ 29 | && cd ${JD_DIR}/panel \ 30 | && npm install \ 31 | && git clone -b ${JD_SCRIPTS_BRANCH} ${JD_SCRIPTS_URL} ${JD_DIR}/scripts \ 32 | && cd ${JD_DIR}/scripts \ 33 | && npm install \ 34 | && npm install -g pm2 \ 35 | && rm -rf /root/.npm 36 | WORKDIR ${JD_DIR} 37 | ENTRYPOINT bash ${JD_DIR}/docker/docker-entrypoint.sh 38 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.0" 2 | services: 3 | # 第1个容器 4 | jd1: 5 | image: thisispatrick/jd-base:v3 6 | container_name: jd1 7 | restart: always 8 | tty: true 9 | network_mode: "bridge" 10 | hostname: jd1 11 | volumes: 12 | - ./jd1/config:/jd/config 13 | - ./jd1/log:/jd/log 14 | - ./jd1/scripts:/jd/scripts 15 | ports: 16 | - 5678:5678 17 | 18 | # 第2个容器 19 | jd2: 20 | image: thisispatrick/jd-base:v3 21 | container_name: jd2 22 | restart: always 23 | tty: true 24 | network_mode: "bridge" 25 | hostname: jd2 26 | volumes: 27 | - ./jd2/config:/jd/config 28 | - ./jd2/log:/jd/log 29 | - ./jd2/scripts:/jd/scripts 30 | ports: 31 | - 5679:5678 32 | 33 | # 第3个容器,以此类推 34 | jd3: 35 | image: thisispatrick/jd-base:v3 36 | container_name: jd3 37 | restart: always 38 | tty: true 39 | network_mode: "bridge" 40 | hostname: jd3 41 | volumes: 42 | - ./jd3/config:/jd/config 43 | - ./jd3/log:/jd/log 44 | - ./jd3/scripts:/jd/scripts 45 | ports: 46 | - 5680:5678 47 | -------------------------------------------------------------------------------- /docker/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo -e "======================1. 检测配置文件========================\n" 5 | [ ! -d ${JD_DIR}/config ] && mkdir -p ${JD_DIR}/config 6 | 7 | if [ ! -s ${JD_DIR}/config/crontab.list ] 8 | then 9 | echo -e "检测到config配置目录下不存在crontab.list或存在但文件为空,从示例文件复制一份用于初始化...\n" 10 | cp -fv ${JD_DIR}/sample/crontab.list.sample ${JD_DIR}/config/crontab.list 11 | sed -i "s,MY_PATH,${JD_DIR},g" ${JD_DIR}/config/crontab.list 12 | sed -i "s,ENV_PATH=,PATH=$PATH,g" ${JD_DIR}/config/crontab.list 13 | fi 14 | crond 15 | crontab ${JD_DIR}/config/crontab.list 16 | echo -e "成功添加定时任务...\n" 17 | 18 | if [ ! -s ${JD_DIR}/config/config.sh ]; then 19 | echo -e "检测到config配置目录下不存在config.sh,从示例文件复制一份用于初始化...\n" 20 | cp -fv ${JD_DIR}/sample/config.sh.sample ${JD_DIR}/config/config.sh 21 | echo 22 | fi 23 | 24 | if [ ! -s ${JD_DIR}/config/auth.json ]; then 25 | echo -e "检测到config配置目录下不存在auth.json,从示例文件复制一份用于初始化...\n" 26 | cp -fv ${JD_DIR}/sample/auth.json ${JD_DIR}/config/auth.json 27 | echo 28 | fi 29 | 30 | echo -e "======================2. 更新源代码========================\n" 31 | bash ${JD_DIR}/git_pull.sh 32 | echo 33 | 34 | echo -e "======================4. 启动挂机程序========================\n" 35 | . ${JD_DIR}/config/config.sh 36 | if [ -n "${Cookie1}" ]; then 37 | bash ${JD_DIR}/jd.sh hangup 2>/dev/null 38 | echo -e "挂机程序启动成功...\n" 39 | else 40 | echo -e "config.sh中还未填入有效的Cookie,可能是首次部署容器,因此不启动挂机程序...\n" 41 | fi 42 | 43 | echo -e "======================5. 启动控制面板========================\n" 44 | pm2 start ${JD_DIR}/panel/server.js 45 | echo -e "控制面板启动成功...\n" 46 | echo -e "请访问 http://:5678 进行配置\n" 47 | echo -e "初始用户名:admin,初始密码:password\n" 48 | 49 | echo -e "\n容器启动成功...\n" 50 | 51 | if [ "${1#-}" != "${1}" ] || [ -z "$(command -v "${1}")" ]; then 52 | set -- node "$@" 53 | fi 54 | 55 | exec "$@" 56 | -------------------------------------------------------------------------------- /docker/onekey-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (C) 2021 Patrick⭐ 4 | # 5 | # 以 Docker 容器的方式一键安装 jd-base。 6 | # 7 | clear 8 | 9 | echo " 10 | ██╗██████╗ ██████╗ ██████╗ ██████╗██╗ ██╗███████╗██████╗ 11 | ██║██╔══██╗ ██╔══██╗██╔═══██╗██╔════╝██║ ██╔╝██╔════╝██╔══██╗ 12 | ██║██║ ██║ ██║ ██║██║ ██║██║ █████╔╝ █████╗ ██████╔╝ 13 | ██ ██║██║ ██║ ██║ ██║██║ ██║██║ ██╔═██╗ ██╔══╝ ██╔══██╗ 14 | ╚█████╔╝██████╔╝ ██████╔╝╚██████╔╝╚██████╗██║ ██╗███████╗██║ ██║ 15 | ╚════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ 16 | 17 | ================ Create by 老竭力 | Mod by Patrick⭐ ================ 18 | " 19 | echo -e "\e[31m警告:请勿将本项目用于任何商业用途!\n\e[0m" 20 | 21 | DockerImage="thisispatrick/jd-base:v3" 22 | ShellName=$0 23 | ShellDir=$(cd "$(dirname "$0")";pwd) 24 | ContainerName="" 25 | PanelPort="" 26 | WorkDir="${ShellDir}/onekey-jd-docker-workdir" 27 | JdDir="" 28 | ConfigDir="" 29 | LogDir="" 30 | ScriptsDir="" 31 | 32 | GetImageType="Online" 33 | HasImage=false 34 | NewImage=true 35 | DelContainer=false 36 | 37 | NeedDirConfig="" 38 | NeedDirLog="" 39 | NeedDirScripts="" 40 | 41 | log() { 42 | echo -e "\e[32m$1 \e[0m" 43 | } 44 | 45 | inp() { 46 | echo -e "\e[33m$1 \e[0m" 47 | } 48 | 49 | warn() { 50 | echo -e "\e[31m$1 \e[0m" 51 | } 52 | 53 | inp "1.运行本脚本前必须自行安装好 Docker" 54 | inp "2.安装过程请务必确保网络通畅" 55 | inp "3.如果什么都不清楚,全部回车使用默认选项即可" 56 | inp "\n按任意键继续,否则按 Ctrl + C 退出!" 57 | read 58 | 59 | # 检查 Docker 环境 60 | Check_Docker() { 61 | if [ ! -x "$(command -v docker)" ]; then 62 | warn "\nDocker 尚未安装!" 63 | exit 1 64 | fi 65 | } 66 | Check_Docker 67 | 68 | # 69 | # 收集配置信息 70 | # 71 | 72 | # 选择镜像获取方式 73 | Choose_GetImageType() { 74 | inp "\n选择镜像获取方式:\n1) 在线获取[默认]\n2) 本地生成" 75 | echo -n -e "\e[33m输入您的选择->\e[0m" 76 | read update 77 | if [ "$update" = "2" ]; then 78 | GetImageType="Local" 79 | fi 80 | } 81 | 82 | # 检测镜像是否存在 83 | Check_Image() { 84 | if [ ! -z "$(docker images -q $DockerImage 2> /dev/null)" ]; then 85 | HasImage=true 86 | inp "检测到先前已经存在的镜像,是否创建新的镜像:\n1) 是[默认]\n2) 不需要" 87 | echo -n -e "\e[33m输入您的选择->\e[0m" 88 | read update 89 | if [ "$update" = "2" ]; then 90 | NewImage=false 91 | else 92 | Choose_GetImageType 93 | fi 94 | else 95 | Choose_GetImageType 96 | fi 97 | } 98 | Check_Image 99 | 100 | # 检测容器是否存在 101 | Check_ContainerName() { 102 | if [ ! -z "$(docker ps -a --format "{{.Names}}" | grep -w $ContainerName 2> /dev/null)" ]; then 103 | inp "\n检测到先前已经存在的容器,是否删除先前的容器:\n1) 是[默认]\n2) 不要" 104 | echo -n -e "\e[33m输入您的选择->\e[0m" 105 | read update 106 | if [ "$update" = "2" ]; then 107 | log "选择了不删除先前的容器,需要重新输入容器名称" 108 | Input_ContainerName 109 | else 110 | DelContainer=true 111 | fi 112 | fi 113 | } 114 | 115 | # 输入容器名称 116 | Input_ContainerName() { 117 | echo -n -e "\n\e[33m请输入要创建的Docker容器名称[默认为:jd]->\e[0m" 118 | read container_name 119 | if [ -z "$container_name" ]; then 120 | ContainerName="jd" 121 | else 122 | ContainerName=$container_name 123 | fi 124 | Check_ContainerName 125 | } 126 | Input_ContainerName 127 | 128 | # 输入端口号 129 | Input_PanelPort() { 130 | echo -n -e "\n\e[33m请输入控制面板端口号[默认为:5678]->\e[0m" 131 | read panel_port 132 | if [ -z "$panel_port" ]; then 133 | PanelPort="5678" 134 | else 135 | PanelPort=$panel_port 136 | fi 137 | inp "如发现端口冲突,请自行检查端口占用情况!" 138 | } 139 | Input_PanelPort 140 | 141 | # 配置文件目录 142 | 143 | Need_ConfigDir() { 144 | inp "\n是否需要映射配置文件目录:\n1) 是[默认]\n2) 否" 145 | echo -n -e "\e[33m输入您的选择->\e[0m" 146 | read need_config_dir 147 | if [ "$need_config_dir" = "2" ]; then 148 | NeedDirConfig='' 149 | else 150 | NeedDirConfig="-v $ConfigDir:/jd/config" 151 | echo -e "\n\e[33m如果有用于存放配置文件的远程 Git 仓库,请输入地址,否则直接回车:\e[0m" 152 | read remote_config 153 | if [ -n "$remote_config" ]; then 154 | git clone $remote_config ${JdDir}/config 155 | else 156 | mkdir -p $ConfigDir 157 | fi 158 | fi 159 | } 160 | 161 | Need_LogDir() { 162 | inp "\n是否需要映射日志文件目录:\n1) 是[默认]\n2) 否" 163 | echo -n -e "\e[33m输入您的选择->\e[0m" 164 | read need_log_dir 165 | if [ "$need_log_dir" = "2" ]; then 166 | NeedDirLog='' 167 | else 168 | NeedDirLog="-v $LogDir:/jd/log" 169 | mkdir -p $LogDir 170 | fi 171 | } 172 | 173 | Need_ScriptsDir() { 174 | inp "\n是否需要映射js脚本目录:\n1) 是\n2) 否[默认]" 175 | echo -n -e "\e[33m输入您的选择->\e[0m" 176 | read need_scripts_dir 177 | if [ "$need_scripts_dir" = "1" ]; then 178 | NeedDirScripts="-v $ScriptsDir:/jd/scripts" 179 | mkdir -p $ScriptsDir 180 | fi 181 | } 182 | 183 | Need_Dir() { 184 | inp "\n是否需要映射文件目录:\n1) 是[默认]\n2) 否" 185 | echo -n -e "\e[33m输入您的选择->\e[0m" 186 | read need_dir 187 | if [ "$need_dir" = "2" ]; then 188 | log "选择了不映射文件目录" 189 | else 190 | echo -e "\n\e[33m请输入配置文件保存的绝对路径,直接回车为 $ShellDir/jd-docker :\e[0m" 191 | read jd_dir 192 | if [ -z "$jd_dir" ]; then 193 | JdDir=$ShellDir/jd-docker 194 | else 195 | JdDir=$jd_dir 196 | fi 197 | ConfigDir=$JdDir/config 198 | LogDir=$JdDir/log 199 | ScriptsDir=$JdDir/scripts 200 | Need_ConfigDir 201 | Need_LogDir 202 | Need_ScriptsDir 203 | fi 204 | } 205 | Need_Dir 206 | 207 | # 208 | # 配置信息收集完成,开始安装 209 | # 210 | 211 | if [ $NewImage = true ]; then 212 | log "\n正在获取新镜像..." 213 | if [ $HasImage = true ]; then 214 | docker image rm -f $DockerImage 215 | fi 216 | if [ $GetImageType = "Local" ]; then 217 | rm -fr $WorkDir 218 | mkdir -p $WorkDir 219 | wget -q https://cdn.jsdelivr.net/gh/RikudouPatrickstar/jd-base/docker/Dockerfile -O $WorkDir/Dockerfile 220 | sed -i 's,github.com,github.com.cnpmjs.org,g' $WorkDir/Dockerfile 221 | sed -i 's,npm install,npm install --registry=https://mirrors.huaweicloud.com/repository/npm/,g' $WorkDir/Dockerfile 222 | docker build -t $DockerImage $WorkDir > $ShellDir/build_jd_image.log 223 | rm -fr $WorkDir 224 | else 225 | docker pull $DockerImage 226 | fi 227 | fi 228 | 229 | if [ $DelContainer = true ]; then 230 | log "\n2.2.删除先前的容器" 231 | docker stop $ContainerName > /dev/null 232 | docker rm $ContainerName > /dev/null 233 | fi 234 | 235 | log "\n创建容器并运行" 236 | docker run -dit \ 237 | $NeedDirConfig \ 238 | $NeedDirLog \ 239 | $NeedDirScripts \ 240 | -p $PanelPort:5678 \ 241 | --name $ContainerName \ 242 | --hostname jd \ 243 | --restart always \ 244 | $DockerImage 245 | 246 | log "\n下面列出所有容器" 247 | docker ps 248 | 249 | log "\n安装已经完成。\n请访问 http://:${PanelPort} 进行配置\n初始用户名:admin,初始密码:password" 250 | rm -f $ShellDir/$ShellName 251 | 252 | -------------------------------------------------------------------------------- /export_sharecodes.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## 路径、环境判断 4 | ShellDir=$(cd "$(dirname "$0")";pwd) 5 | LogDir=${ShellDir}/log 6 | ConfigDir=${ShellDir}/config 7 | FileConf=${ConfigDir}/config.sh 8 | Tips="# 未从日志中匹配到任何互助码" 9 | 10 | ## 所有有互助码的活动,只需要把脚本名称去掉前缀 jd_ 后列在 Name1 中,将其中文名称列在 Name2 中,对应 config.sh 中互助码后缀列在 Name3 中即可。 11 | ## Name1、Name2 和 Name3 中的三个名称必须一一对应。 12 | Name1=(fruit pet plantBean dreamFactory jdfactory crazy_joy jdzz jxnc bookshop cash sgmh cfd global) 13 | Name2=(东东农场 东东萌宠 京东种豆得豆 京喜工厂 东东工厂 crazyJoy任务 京东赚赚 京喜农场 口袋书店 签到领现金 闪购盲盒 京喜财富岛 环球挑战赛) 14 | Name3=(Fruit Pet Bean DreamFactory JdFactory Joy Jdzz Jxnc BookShop Cash Sgmh Cfd Global) 15 | 16 | 17 | ## 导入 config.sh 18 | function Import_Conf { 19 | if [ -f ${FileConf} ] 20 | then 21 | . ${FileConf} 22 | if [ -z "${Cookie1}" ]; then 23 | echo -e "# 请先在 config.sh 中配置好 Cookie\n" 24 | exit 1 25 | fi 26 | else 27 | echo -e "# 配置文件 ${FileConf} 不存在,请先按教程配置好该文件\n" 28 | exit 1 29 | fi 30 | } 31 | 32 | 33 | ## 用户数量 UserSum 34 | function Count_UserSum { 35 | for ((i=1; i<=35; i++)); do 36 | Tmp=Cookie$i 37 | CookieTmp=${!Tmp} 38 | [[ ${CookieTmp} ]] && UserSum=$i || break 39 | done 40 | } 41 | 42 | 43 | ## 导出互助码的通用程序 44 | function Cat_Scodes { 45 | if [ -d ${LogDir}/jd_$1 ] && [[ $(ls ${LogDir}/jd_$1) != "" ]]; then 46 | cd ${LogDir}/jd_$1 47 | 48 | ## 导出助力码变量(My) 49 | for log in $(ls -r); do 50 | codes=$(grep -E $3 ${log} | uniq | sed -r "s/【京东账号/My$2/;s/(.*?】/='/;s/$/'/") 51 | if [[ ${codes} ]]; then 52 | ## 添加判断,若未找到该用户互助码,则设置为空值 53 | for ((user_num=1;user_num<=${UserSum};user_num++)); do 54 | echo -e "${codes}" | grep -Eq "My$2${user_num}=" 55 | if [ $? -eq 1 ]; then 56 | if [ $user_num == 1 ]; then 57 | codes=$(echo "${codes}" | sed -r "1i My${2}1=''") 58 | else 59 | codes=$(echo "${codes}" | sed -r "/My$2$(expr ${user_num} - 1)=/a\My$2${user_num}=''") 60 | fi 61 | fi 62 | done 63 | break 64 | fi 65 | done 66 | 67 | ## 导出为他人助力变量(ForOther) 68 | if [[ ${codes} ]]; then 69 | help_code="" 70 | for ((user_num=1;user_num<=${UserSum};user_num++)); do 71 | echo -e "${codes}" | grep -Eq "My$2${user_num}=''" 72 | if [ $? -eq 1 ]; then 73 | help_code=${help_code}"\${My"$2${user_num}"}@" 74 | fi 75 | done 76 | ## 生成互助规则模板 77 | for_other_codes="" 78 | case $HelpType in 79 | 0) ### 统一优先级助力模板 80 | new_code=$(echo ${help_code} | sed "s/@$//") 81 | for ((user_num=1;user_num<=${UserSum};user_num++)); do 82 | if [ $user_num == 1 ]; then 83 | for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\""${new_code}"\"\n" 84 | else 85 | for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\"\${ForOther"${2}1"}\"\n" 86 | fi 87 | done 88 | ;; 89 | 1) ### 均匀助力模板 90 | for ((user_num=1;user_num<=${UserSum};user_num++)); do 91 | echo ${help_code} | grep "\${My"$2${user_num}"}@" > /dev/null 92 | if [ $? -eq 0 ]; then 93 | left_str=$(echo ${help_code} | sed "s/\${My$2${user_num}}@/ /g" | awk '{print $1}') 94 | right_str=$(echo ${help_code} | sed "s/\${My$2${user_num}}@/ /g" | awk '{print $2}') 95 | mark="\${My$2${user_num}}@" 96 | else 97 | left_str=$(echo ${help_code} | sed "s/${mark}/ /g" | awk '{print $1}')${mark} 98 | right_str=$(echo ${help_code} | sed "s/${mark}/ /g" | awk '{print $2}') 99 | fi 100 | new_code=$(echo ${right_str}${left_str} | sed "s/@$//") 101 | for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\""${new_code}"\"\n" 102 | done 103 | ;; 104 | *) ### 普通优先级助力模板 105 | for ((user_num=1;user_num<=${UserSum};user_num++)); do 106 | new_code=$(echo ${help_code} | sed "s/\${My"$2${user_num}"}@//;s/@$//") 107 | for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\""${new_code}"\"\n" 108 | done 109 | ;; 110 | esac 111 | 112 | echo -e "${codes}\n\n${for_other_codes}" | sed "s/[[:space:]]//g" 113 | else 114 | echo ${Tips} 115 | fi 116 | 117 | else 118 | echo "# 未运行过 jd_$1 脚本,未产生日志" 119 | fi 120 | } 121 | 122 | 123 | ## 汇总 124 | function Cat_All { 125 | echo -e "\n# 从最后一个日志提取互助码,受日志内容影响,仅供参考。" 126 | for ((i=0; i<${#Name1[*]}; i++)); do 127 | echo -e "\n\n## ${Name2[i]}:" 128 | Cat_Scodes "${Name1[i]}" "${Name3[i]}" "的${Name2[i]}好友互助码" 129 | done 130 | } 131 | 132 | 133 | ## 执行并写入日志 134 | LogTime=$(date "+%Y-%m-%d-%H-%M-%S") 135 | LogFile="${LogDir}/export_sharecodes/${LogTime}.log" 136 | [ ! -d "${LogDir}/export_sharecodes" ] && mkdir -p ${LogDir}/export_sharecodes 137 | Import_Conf && Count_UserSum && Cat_All | perl -pe "{s|京东种豆|种豆|; s|crazyJoy任务|疯狂的JOY|}" | tee ${LogFile} 138 | -------------------------------------------------------------------------------- /git_pull.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## 文件路径、脚本网址、文件版本以及各种环境的判断 4 | ShellDir=$(cd "$(dirname "$0")";pwd) 5 | ShellJd=${ShellDir}/jd.sh 6 | LogDir=${ShellDir}/log 7 | [ ! -d ${LogDir} ] && mkdir -p ${LogDir} 8 | ScriptsDir=${ShellDir}/scripts 9 | ConfigDir=${ShellDir}/config 10 | FileConf=${ConfigDir}/config.sh 11 | FileConfSample=${ShellDir}/sample/config.sh.sample 12 | ListCron=${ConfigDir}/crontab.list 13 | ListCronLxk=${ScriptsDir}/docker/crontab_list.sh 14 | ListTask=${LogDir}/task.list 15 | ListJs=${LogDir}/js.list 16 | ListJsAdd=${LogDir}/js-add.list 17 | ListJsDrop=${LogDir}/js-drop.list 18 | ContentVersion=${ShellDir}/version 19 | ContentNewTask=${ShellDir}/new_task 20 | ContentDropTask=${ShellDir}/drop_task 21 | SendCount=${ShellDir}/send_count 22 | ScriptsURL=https://github.com.cnpmjs.org/RikudouPatrickstar/jd_scripts 23 | ShellURL=https://github.com.cnpmjs.org/RikudouPatrickstar/jd-base 24 | 25 | 26 | ## 导入配置文件 27 | function Import_Conf { 28 | if [ -f ${FileConf} ]; then 29 | . ${FileConf} 30 | fi 31 | } 32 | 33 | 34 | ## 更新 jd-base 脚本 35 | function Git_PullShell { 36 | echo -e "更新 jd-base 脚本\n" 37 | cd ${ShellDir} 38 | git fetch --all 39 | ExitStatusShell=$? 40 | git reset --hard origin/v3 41 | echo 42 | } 43 | 44 | 45 | ## 更新 jd-base 脚本成功后的操作 46 | function Git_PullShellNext { 47 | if [[ ${ExitStatusShell} -eq 0 ]]; then 48 | echo -e "更新 jd-base 脚本成功\n" 49 | [[ "${PanelDependOld}" != "${PanelDependNew}" ]] && cd ${ShellDir}/panel && Npm_Install panel 50 | Notify_Version 51 | else 52 | echo -e "更新 jd-base 脚本失败,请检查原因\n" 53 | fi 54 | } 55 | 56 | 57 | ## 克隆 jd_scripts 脚本 58 | function Git_CloneScripts { 59 | echo -e "克隆 jd_scripts 脚本\n" 60 | git clone -b master ${ScriptsURL} ${ScriptsDir} 61 | ExitStatusScripts=$? 62 | echo 63 | } 64 | 65 | 66 | ## 更新 jd_scripts 脚本 67 | function Git_PullScripts { 68 | echo -e "更新 jd_scripts 脚本\n" 69 | cd ${ScriptsDir} 70 | git fetch --all 71 | ExitStatusScripts=$? 72 | git reset --hard origin/master 73 | echo 74 | } 75 | 76 | 77 | ## 给所有 shell 脚本赋予 755 权限 78 | function Chmod_ShellScripts { 79 | shfiles=$(find ${ShellDir} 2> /dev/null) 80 | for shf in ${shfiles}; do 81 | if [ ${shf##*.} == 'sh' ]; then 82 | chmod 755 ${shf} 83 | fi 84 | done 85 | } 86 | 87 | 88 | ## 获取用户数量 UserSum 89 | function Count_UserSum { 90 | for ((i=1; i<=35; i++)); do 91 | Tmp=Cookie$i 92 | CookieTmp=${!Tmp} 93 | [[ ${CookieTmp} ]] && UserSum=$i || break 94 | done 95 | 96 | for ((d=36; d<=1000; d++)); do 97 | Del=Cookie$d 98 | sed -i "/${!Del}/d" ${FileConf} || break 99 | done 100 | } 101 | 102 | 103 | ## 检测文件:远程仓库 jd_scripts 中的 docker/crontab_list.sh 104 | ## 检测定时任务是否有变化,此函数会在 log 文件夹下生成四个文件,分别为: 105 | ## task.list crontab.list 中的所有任务清单,仅保留脚本名 106 | ## js.list 上述检测文件中用来运行 jd_scripts 脚本的清单(去掉后缀.js,非运行脚本的不会包括在内) 107 | ## js-add.list 如果上述检测文件增加了定时任务,这个文件内容将不为空 108 | ## js-drop.list 如果上述检测文件删除了定时任务,这个文件内容将不为空 109 | function Diff_Cron { 110 | if [ -f ${ListCron} ]; then 111 | grep "${ShellDir}/" ${ListCron} | grep -E " j[drx]_\w+" | perl -pe "s|.+ (j[drx]_\w+).*|\1|" | sort -u > ${ListTask} 112 | cat ${ListCronLxk} | grep -E "j[drx]_\w+\.js" | perl -pe "s|.+(j[drx]_\w+)\.js.+|\1|" | sort -u > ${ListJs} 113 | grep -vwf ${ListTask} ${ListJs} > ${ListJsAdd} 114 | grep -vwf ${ListJs} ${ListTask} > ${ListJsDrop} 115 | else 116 | echo -e "${ListCron} 文件不存在,请先定义你自己的 crontab.list\n" 117 | fi 118 | } 119 | 120 | 121 | ## 发送删除失效定时任务的消息 122 | function Notify_DropTask { 123 | cd ${ShellDir} 124 | node update.js 125 | [ -f ${ContentDropTask} ] && rm -f ${ContentDropTask} 126 | } 127 | 128 | 129 | ## 发送新的定时任务消息 130 | function Notify_NewTask { 131 | cd ${ShellDir} 132 | node update.js 133 | [ -f ${ContentNewTask} ] && rm -f ${ContentNewTask} 134 | } 135 | 136 | 137 | ## 检测配置文件版本 138 | function Notify_Version { 139 | ## 识别出两个文件的版本号 140 | VerConfSample=$(grep " Version: " ${FileConfSample} | perl -pe "s|.+v((\d+\.?){3})|\1|") 141 | [ -f ${FileConf} ] && VerConf=$(grep " Version: " ${FileConf} | perl -pe "s|.+v((\d+\.?){3})|\1|") 142 | 143 | ## 删除旧的发送记录文件 144 | [ -f "${SendCount}" ] && [[ $(cat ${SendCount}) != ${VerConfSample} ]] && rm -f ${SendCount} 145 | 146 | ## 识别出更新日期和更新内容 147 | UpdateDate=$(grep " Date: " ${FileConfSample} | awk -F ": " '{print $2}') 148 | UpdateContent=$(grep " Update Content: " ${FileConfSample} | awk -F ": " '{print $2}') 149 | 150 | ## 如果是今天,并且版本号不一致,则发送通知 151 | if [ -f ${FileConf} ] && [[ "${VerConf}" != "${VerConfSample}" ]] && [[ ${UpdateDate} == $(date "+%Y-%m-%d") ]] 152 | then 153 | if [ ! -f ${SendCount} ]; then 154 | echo -e "日期: ${UpdateDate}\n版本: ${VerConf} -> ${VerConfSample}\n内容: ${UpdateContent}\n\n" | tee ${ContentVersion} 155 | echo -e "如需更新请手动操作,仅更新当天通知一次!" >> ${ContentVersion} 156 | cd ${ShellDir} 157 | node update.js 158 | if [ $? -eq 0 ]; then 159 | echo "${VerConfSample}" > ${SendCount} 160 | [ -f ${ContentVersion} ] && rm -f ${ContentVersion} 161 | fi 162 | fi 163 | else 164 | [ -f ${ContentVersion} ] && rm -f ${ContentVersion} 165 | [ -f ${SendCount} ] && rm -f ${SendCount} 166 | fi 167 | } 168 | 169 | 170 | ## npm install 子程序,判断是否安装有 yarn 171 | function Npm_InstallSub { 172 | if ! type yarn >/dev/null 2>&1 173 | then 174 | npm install --registry=https://mirrors.huaweicloud.com/repository/npm/ || npm install 175 | else 176 | echo -e "检测到本机安装了 yarn,使用 yarn 替代 npm\n" 177 | yarn install --registry=https://mirrors.huaweicloud.com/repository/npm/ || yarn install 178 | fi 179 | } 180 | 181 | 182 | ## npm install 183 | function Npm_Install { 184 | echo -e "检测到 $1 的依赖包有变化,运行 npm install\n" 185 | Npm_InstallSub 186 | if [ $? -ne 0 ]; then 187 | echo -e "\nnpm install 运行不成功,自动删除 $1/node_modules 后再次尝试一遍" 188 | rm -rf node_modules 189 | fi 190 | echo 191 | 192 | if [ ! -d node_modules ]; then 193 | echo -e "运行 npm install\n" 194 | Npm_InstallSub 195 | if [ $? -ne 0 ]; then 196 | echo -e "\nnpm install 运行不成功,自动删除 $1/node_modules\n" 197 | echo -e "请进入 $1 目录后手动运行 npm install\n" 198 | echo -e "3s\n" 199 | sleep 1 200 | echo -e "2s\n" 201 | sleep 1 202 | echo -e "1s\n" 203 | sleep 1 204 | rm -rf node_modules 205 | fi 206 | fi 207 | } 208 | 209 | 210 | ## 输出是否有新的定时任务 211 | function Output_ListJsAdd { 212 | if [ -s ${ListJsAdd} ]; then 213 | echo -e "检测到有新的定时任务:\n" 214 | cat ${ListJsAdd} 215 | echo 216 | fi 217 | } 218 | 219 | 220 | ## 输出是否有失效的定时任务 221 | function Output_ListJsDrop { 222 | if [ ${ExitStatusScripts} -eq 0 ] && [ -s ${ListJsDrop} ]; then 223 | echo -e "检测到有失效的定时任务:\n" 224 | cat ${ListJsDrop} 225 | echo 226 | fi 227 | } 228 | 229 | 230 | ## 自动删除失效的定时任务,需要5个条件: 231 | ## 1. AutoAddCron 设置为 true 232 | ## 2. 正常更新 jd_scripts 脚本,没有报错 233 | ## 3. js-drop.list 不为空 234 | ## 4. crontab.list 存在并且不为空 235 | ## 5. 已经正常运行过 npm install 236 | ## 检测文件:远程仓库 jd_scripts 中的 docker/crontab_list.sh 237 | ## 如果检测到某个定时任务在上述检测文件中已删除,那么在本地也删除对应定时任务 238 | function Del_Cron { 239 | if [ "${AutoDelCron}" = "true" ] && [ -s ${ListJsDrop} ] && [ -s ${ListCron} ] && [ -d ${ScriptsDir}/node_modules ]; then 240 | echo -e "开始尝试自动删除定时任务如下:\n" 241 | cat ${ListJsDrop} 242 | echo 243 | JsDrop=$(cat ${ListJsDrop}) 244 | for Cron in ${JsDrop} 245 | do 246 | perl -i -ne "{print unless / ${Cron}( |$)/}" ${ListCron} 247 | done 248 | crontab ${ListCron} 249 | echo -e "成功删除失效的脚本与定时任务\n\n" 250 | if [ -d ${ScriptsDir}/node_modules ]; then 251 | echo -e "删除失效的定时任务:\n\n${JsDrop}" > ${ContentDropTask} 252 | Notify_DropTask 253 | fi 254 | fi 255 | } 256 | 257 | 258 | ## 自动增加新的定时任务,需要5个条件: 259 | ## 1. AutoAddCron 设置为 true 260 | ## 2. 正常更新 jd_scripts 脚本,没有报错 261 | ## 3. js-add.list 不为空 262 | ## 4. crontab.list 存在并且不为空 263 | ## 5. 已经正常运行过 npm install 264 | ## 检测文件:远程仓库 jd_scripts 中的 docker/crontab_list.sh 265 | ## 如果检测到检测文件中增加新的定时任务,那么在本地也增加 266 | ## 本功能生效时,会自动从检测文件新增加的任务中读取时间,该时间为北京时间 267 | function Add_Cron { 268 | if [ "${AutoAddCron}" = "true" ] && [ -s ${ListJsAdd} ] && [ -s ${ListCron} ] && [ -d ${ScriptsDir}/node_modules ]; then 269 | echo -e "开始尝试自动添加定时任务如下:\n" 270 | cat ${ListJsAdd} 271 | echo 272 | JsAdd=$(cat ${ListJsAdd}) 273 | 274 | for Cron in ${JsAdd} 275 | do 276 | if [[ ${Cron} == jd_bean_sign ]] 277 | then 278 | echo "4 0,9 * * * bash ${ShellJd} ${Cron}" >> ${ListCron} 279 | else 280 | cat ${ListCronLxk} | grep -E "\/${Cron}\." | perl -pe "s|(^.+)node */scripts/(j[drx]_\w+)\.js.+|\1bash ${ShellJd} \2|" >> ${ListCron} 281 | fi 282 | done 283 | 284 | if [ $? -eq 0 ] 285 | then 286 | crontab ${ListCron} 287 | echo -e "成功添加新的定时任务\n\n" 288 | if [ -d ${ScriptsDir}/node_modules ]; then 289 | echo -e "成功添加新的定时任务:\n\n${JsAdd}" > ${ContentNewTask} 290 | Notify_NewTask 291 | fi 292 | else 293 | echo -e "添加新的定时任务出错,请手动添加\n" 294 | if [ -d ${ScriptsDir}/node_modules ]; then 295 | echo -e "尝试自动添加以下新的定时任务出错,请手动添加:\n\n${JsAdd}" > ${ContentNewTask} 296 | Notify_NewTask 297 | fi 298 | fi 299 | fi 300 | } 301 | 302 | ## 为额外的 js 脚本存放目录配置 lxk0301/jd_scripts 环境 303 | function Set_DiyEnv { 304 | echo -e "\n--------------------------------------------------------------\n" 305 | echo -e "设置额外的 js 脚本环境\n" 306 | EnvFiles=( 307 | Env.min.js 308 | JS_USER_AGENTS.js 309 | USER_AGENTS.js 310 | index.js 311 | jdCookie.js 312 | sendNotify.js 313 | ) 314 | if [[ ${DiyDirs} ]]; then 315 | for ((i=0; i<${#DiyDirs[*]}; i++)); do 316 | [ ! -d ${DiyDirs[i]} ] && mkdir -p ${DiyDirs[i]} 317 | for env_file in ${EnvFiles[*]}; do 318 | cp -f ${ScriptsDir}/${env_file} ${ShellDir}/${DiyDirs[i]}/ 319 | done 320 | [ -f ${ShellDir}/${DiyDirs[i]}/package.json ] && DiyDependOld=$(cat ${ShellDir}/${DiyDirs[i]}/package.json) 321 | if [ ${DiyPackgeJson} == "false" ]; then 322 | cp -f ${ScriptsDir}/package.json ${ShellDir}/${DiyDirs[i]}/ 323 | fi 324 | [ -f ${ShellDir}/${DiyDirs[i]}/package.json ] && DiyDependNew=$(cat ${ShellDir}/${DiyDirs[i]}/package.json) 325 | if [ "${DiyDependOld}" != "${DiyDependNew}" ] || [ ! -d ${ShellDir}/${DiyDirs[i]}/node_modules ];then 326 | cd ${ShellDir}/${DiyDirs[i]} && Npm_Install ${DiyDirs[i]} 327 | fi 328 | done 329 | else 330 | echo -e "未设置额外脚本目录,跳过\n" 331 | fi 332 | } 333 | 334 | 335 | ## 替换 jd_scripts 中的 js 脚本 336 | function ReplaceJs { 337 | echo -e "\n--------------------------------------------------------------\n" 338 | echo -e "替换 js 脚本\n" 339 | if [[ ${ReplaceJsName} ]] && [[ ${ReplaceJsUrl} ]]; then 340 | for ((i=0; i<${#ReplaceJsName[*]}; i++)); do 341 | cd ${ScriptsDir} 342 | rm -f ${ReplaceJsName[i]}.js 343 | wget -q ${ReplaceJsUrl[i]} -O ${ReplaceJsName[i]}.js 344 | if [ $? == '0' ]; then 345 | echo -e "${ReplaceJsName[i]}.js 替换成功\n" 346 | else 347 | echo -e "${ReplaceJsName[i]}.js 替换失败,请检查原因\n" 348 | fi 349 | done 350 | else 351 | echo -e "未设置替换,跳过\n" 352 | fi 353 | } 354 | 355 | 356 | ## 在日志中记录时间与路径 357 | echo -e "\n##############################################################\n" 358 | echo -n "系统时间:" 359 | echo -e "$(date "+%Y-%m-%d %H:%M:%S")" 360 | if [ "${TZ}" = "UTC" ]; then 361 | echo -n "北京时间:" 362 | echo -e "$(date -d "8 hour" "+%Y-%m-%d %H:%M:%S")" 363 | fi 364 | echo -e "\n--------------------------------------------------------------\n" 365 | 366 | ## 导入配置,设置远程仓库地址,更新 jd-base 脚本,发送新配置通知 367 | Import_Conf "git_pull" 368 | [ -f ${ShellDir}/panel/package.json ] && PanelDependOld=$(cat ${ShellDir}/panel/package.json) 369 | Git_PullShell 370 | [ -f ${ShellDir}/panel/package.json ] && PanelDependNew=$(cat ${ShellDir}/panel/package.json) 371 | Git_PullShellNext 372 | 373 | echo -e "\n--------------------------------------------------------------\n" 374 | ## 克隆或更新 jd_scripts 脚本 375 | [ -f ${ScriptsDir}/package.json ] && ScriptsDependOld=$(cat ${ScriptsDir}/package.json) 376 | [ -d ${ScriptsDir}/.git ] && Git_PullScripts || Git_CloneScripts 377 | [ -f ${ScriptsDir}/package.json ] && ScriptsDependNew=$(cat ${ScriptsDir}/package.json) 378 | 379 | ## 执行各函数 380 | if [[ ${ExitStatusScripts} -eq 0 ]] 381 | then 382 | echo -e "更新 jd_scripts 脚本成功\n" 383 | sed -i '/本脚本开源免费使用 By/d' ${ScriptsDir}/sendNotify.js 384 | Diff_Cron 385 | [[ "${ScriptsDependOld}" != "${ScriptsDependNew}" ]] && cd ${ScriptsDir} && Npm_Install scripts 386 | Output_ListJsAdd 387 | Output_ListJsDrop 388 | Del_Cron 389 | Add_Cron 390 | ReplaceJs 391 | Set_DiyEnv 392 | else 393 | echo -e "更新 jd_scripts 脚本失败,请检查原因\n" 394 | fi 395 | 396 | ## 给所有 shell 脚本赋予 755 权限 397 | Chmod_ShellScripts 398 | -------------------------------------------------------------------------------- /jd.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## 路径 4 | ShellDir=$(cd "$(dirname "$0")";pwd) 5 | ConfigDir=${ShellDir}/config 6 | FileConf=${ConfigDir}/config.sh 7 | ScriptsDir=${ShellDir}/scripts 8 | LogDir=${ShellDir}/log 9 | ListScripts=($(cd ${ScriptsDir}; ls *.js | grep -E "j[drx]_")) 10 | ListCron=${ConfigDir}/crontab.list 11 | 12 | 13 | ## 导入 config.sh 14 | function Import_Conf { 15 | if [ -f ${FileConf} ] 16 | then 17 | . ${FileConf} 18 | if [ -z "${Cookie1}" ]; then 19 | echo -e "请先在 config.sh 中配置好 Cookie\n" 20 | exit 1 21 | fi 22 | else 23 | echo -e "配置文件 ${FileConf} 不存在,请先按教程配置好该文件\n" 24 | exit 1 25 | fi 26 | } 27 | 28 | 29 | ## 更新 Crontab 30 | function Detect_Cron { 31 | if [[ $(cat ${ListCron}) != $(crontab -l) ]]; then 32 | crontab ${ListCron} 33 | fi 34 | } 35 | 36 | 37 | ## 用户数量 UserSum 38 | function Count_UserSum { 39 | for ((i=1; i<=35; i++)); do 40 | Tmp=Cookie$i 41 | CookieTmp=${!Tmp} 42 | [[ ${CookieTmp} ]] && UserSum=$i || break 43 | done 44 | } 45 | 46 | 47 | ## 组合 Cookie 和互助码子程序 48 | function Combin_Sub { 49 | CombinAll="" 50 | for ((i=1; i<=${UserSum}; i++)); do 51 | for num in ${TempBlockCookie}; do 52 | if [[ $i -eq $num ]]; then 53 | continue 2 54 | fi 55 | done 56 | Tmp1=$1$i 57 | Tmp2=${!Tmp1} 58 | CombinAll="${CombinAll}&${Tmp2}" 59 | done 60 | echo ${CombinAll} | perl -pe "{s|^&||; s|^@+||; s|&@|&|g; s|@+&|&|g; s|@+|@|g; s|@+$||}" 61 | } 62 | 63 | 64 | ## 组合 Cookie、Token 与互助码 65 | function Combin_All { 66 | export JD_COOKIE=$(Combin_Sub Cookie) 67 | export FRUITSHARECODES=$(Combin_Sub ForOtherFruit) 68 | export PETSHARECODES=$(Combin_Sub ForOtherPet) 69 | export PLANT_BEAN_SHARECODES=$(Combin_Sub ForOtherBean) 70 | export DREAM_FACTORY_SHARE_CODES=$(Combin_Sub ForOtherDreamFactory) 71 | export DDFACTORY_SHARECODES=$(Combin_Sub ForOtherJdFactory) 72 | export JDZZ_SHARECODES=$(Combin_Sub ForOtherJdzz) 73 | export JDJOY_SHARECODES=$(Combin_Sub ForOtherJoy) 74 | export JXNC_SHARECODES=$(Combin_Sub ForOtherJxnc) 75 | export BOOKSHOP_SHARECODES=$(Combin_Sub ForOtherBookShop) 76 | export JD_CASH_SHARECODES=$(Combin_Sub ForOtherCash) 77 | export JDSGMH_SHARECODES=$(Combin_Sub ForOtherSgmh) 78 | export JDCFD_SHARECODES=$(Combin_Sub ForOtherCfd) 79 | export JDGLOBAL_SHARECODES=$(Combin_Sub ForOtherGlobal) 80 | } 81 | 82 | 83 | ## 转换 JD_BEAN_SIGN_STOP_NOTIFY 或 JD_BEAN_SIGN_NOTIFY_SIMPLE 84 | function Trans_JD_BEAN_SIGN_NOTIFY { 85 | case ${NotifyBeanSign} in 86 | 0) 87 | export JD_BEAN_SIGN_STOP_NOTIFY="true" 88 | ;; 89 | 2) 90 | export JD_BEAN_SIGN_STOP_NOTIFY="false" 91 | export JD_BEAN_SIGN_NOTIFY_SIMPLE="false" 92 | ;; 93 | *) 94 | export JD_BEAN_SIGN_STOP_NOTIFY="false" 95 | export JD_BEAN_SIGN_NOTIFY_SIMPLE="true" 96 | ;; 97 | esac 98 | } 99 | 100 | 101 | ## 转换 UN_SUBSCRIBES 102 | function Trans_UN_SUBSCRIBES { 103 | export UN_SUBSCRIBES="${goodPageSize}\n${shopPageSize}\n${jdUnsubscribeStopGoods}\n${jdUnsubscribeStopShop}" 104 | } 105 | 106 | 107 | ## 设置获取共享池助力码个数 108 | function Get_HelpPoolNum { 109 | HelpPoolNum=$( printf "%d" "$HelpPoolNum" 2> /dev/null ) 110 | if [ $HelpPoolNum -lt 0 ] || [ $HelpPoolNum -gt 25 ]; then 111 | HelpPoolNum=0 112 | fi 113 | HelpPoolNum16=0x$( printf %x $HelpPoolNum ) 114 | } 115 | 116 | 117 | ## 申明全部变量 118 | function Set_Env { 119 | Count_UserSum 120 | Combin_All 121 | Trans_JD_BEAN_SIGN_NOTIFY 122 | Trans_UN_SUBSCRIBES 123 | Get_HelpPoolNum 124 | } 125 | 126 | 127 | ## 随机延迟 128 | function Random_Delay { 129 | if [[ -n ${RandomDelay} ]] && [[ ${RandomDelay} -gt 0 ]]; then 130 | CurMin=$(date "+%-M") 131 | if [[ ${CurMin} -gt 2 && ${CurMin} -lt 30 ]] || [[ ${CurMin} -gt 31 && ${CurMin} -lt 59 ]]; then 132 | CurDelay=$((${RANDOM} % ${RandomDelay} + 1)) 133 | echo -e "\n命令未添加 \"now\",随机延迟 ${CurDelay} 秒后再执行任务,如需立即终止,请按 CTRL+C\n" 134 | sleep ${CurDelay} 135 | fi 136 | fi 137 | } 138 | 139 | 140 | ## 使用说明 141 | function Help { 142 | echo -e "本脚本的用法为:" 143 | echo -e "1. bash jd.sh xxx # 如果设置了随机延迟并且当时时间不在 0-2、30-31、59 分内,将随机延迟一定秒数" 144 | echo -e "2. bash jd.sh xxx now # 无论是否设置了随机延迟,均立即运行" 145 | echo -e "3. bash jd.sh hangup # 重启挂机程序" 146 | echo -e "4. bash jd.sh resetpwd # 重置控制面板用户名和密码" 147 | echo -e "\n针对用法1、用法2中的 \"xxx\",无需输入后缀 \".js\",另外,如果前缀是 \"jd_\" 的话前缀也可以省略。" 148 | echo -e "当前有以下脚本可以运行(仅列出 jd_scripts 中以 jd_、jr_、jx_ 开头的脚本):" 149 | cd ${ScriptsDir} 150 | for ((i=0; i<${#ListScripts[*]}; i++)); do 151 | Name=$(grep "new Env" ${ListScripts[i]} | awk -F "'|\"" '{print $2}') 152 | echo -e "$(($i + 1)).${Name}:${ListScripts[i]}" 153 | done 154 | } 155 | 156 | 157 | ## 查找脚本路径与准确的文件名 158 | function Find_FileDir { 159 | FileNameTmp1=$(echo $1 | perl -pe "s|\.js||") 160 | FileNameTmp2=$(echo $1 | perl -pe "{s|jd_||; s|\.js||; s|^|jd_|}") 161 | for ((i=0; i<${#DiyDirs[*]}; i++)); do 162 | DiyDirs[i]=${ShellDir}/${DiyDirs[i]} 163 | done 164 | SeekDir="${ScriptsDir} ${ScriptsDir}/backUp ${DiyDirs[*]}" 165 | FileName="" 166 | WhichDir="" 167 | 168 | for dir in ${SeekDir} 169 | do 170 | if [ -f ${dir}/${FileNameTmp1}.js ]; then 171 | FileName=${FileNameTmp1} 172 | WhichDir=${dir} 173 | break 174 | elif [ -f ${dir}/${FileNameTmp2}.js ]; then 175 | FileName=${FileNameTmp2} 176 | WhichDir=${dir} 177 | break 178 | fi 179 | done 180 | } 181 | 182 | 183 | ## nohup 184 | function Run_Nohup { 185 | nohup node $1.js 2>&1 > ${LogFile} & 186 | } 187 | 188 | 189 | ## 运行挂机脚本 190 | function Run_HangUp { 191 | HangUpJs="jd_crazy_joy_coin" 192 | cd ${ScriptsDir} 193 | for js in ${HangUpJs}; do 194 | Import_Conf ${js} 195 | Count_UserSum 196 | Set_Env 197 | if type pm2 >/dev/null 2>&1; then 198 | pm2 stop ${js}.js 2>/dev/null 199 | pm2 flush 200 | pm2 start -a ${js}.js --watch "${ScriptsDir}/${js}.js" --name="${js}" 201 | else 202 | if [ $(. /etc/os-release && echo "$ID") == "openwrt" ]; then 203 | if [[ $(ps | grep "${js}" | grep -v "grep") != "" ]]; then 204 | ps | grep "${js}" | grep -v "grep" | awk '{print $1}' | xargs kill -9 205 | fi 206 | else 207 | if [[ $(ps -ef | grep "${js}" | grep -v "grep") != "" ]]; then 208 | ps -ef | grep "${js}" | grep -v "grep" | awk '{print $2}' | xargs kill -9 209 | fi 210 | fi 211 | [ ! -d ${LogDir}/${js} ] && mkdir -p ${LogDir}/${js} 212 | LogTime=$(date "+%Y-%m-%d-%H-%M-%S") 213 | LogFile="${LogDir}/${js}/${LogTime}.log" 214 | Run_Nohup ${js} >/dev/null 2>&1 215 | fi 216 | done 217 | } 218 | 219 | 220 | ## 重置密码 221 | function Reset_Pwd { 222 | cp -f ${ShellDir}/sample/auth.json ${ConfigDir}/auth.json 223 | echo -e "控制面板重置成功,用户名:admin,密码:password\n" 224 | } 225 | 226 | 227 | ## 运行京东脚本 228 | function Run_Normal { 229 | Import_Conf $1 230 | Detect_Cron 231 | Count_UserSum 232 | Find_FileDir $1 233 | Set_Env 234 | 235 | if [[ ${FileName} ]] && [[ ${WhichDir} ]] 236 | then 237 | [ $# -eq 1 ] && Random_Delay 238 | LogTime=$(date "+%Y-%m-%d-%H-%M-%S") 239 | LogFile="${LogDir}/${FileName}/${LogTime}.log" 240 | [ ! -d ${LogDir}/${FileName} ] && mkdir -p ${LogDir}/${FileName} 241 | cd ${WhichDir} 242 | sed -i "s/randomCount = .* [0-9]* : [0-9]*;/randomCount = $HelpPoolNum;/g" ${FileName}.js 243 | sed -i "s/randomCount=.*?0x[0-9a-f]*:0x[0-9a-f]*;/randomCount=$HelpPoolNum16;/g" ${FileName}.js 244 | node ${FileName}.js 2>&1 | tee ${LogFile} 245 | else 246 | echo -e "\n在有关目录下均未检测到 $1 脚本的存在\n" 247 | Help 248 | fi 249 | } 250 | 251 | 252 | ## 命令检测 253 | case $# in 254 | 0) 255 | echo 256 | Help 257 | ;; 258 | 1) 259 | case $1 in 260 | hangup) 261 | Run_HangUp 262 | ;; 263 | resetpwd) 264 | Reset_Pwd 265 | ;; 266 | *) 267 | Run_Normal $1 268 | ;; 269 | esac 270 | ;; 271 | 2) 272 | case $2 in 273 | now) 274 | Run_Normal $1 $2 275 | ;; 276 | *) 277 | echo -e "\n命令输入错误\n" 278 | Help 279 | ;; 280 | esac 281 | ;; 282 | *) 283 | echo -e "\n命令输入错误\n" 284 | Help 285 | ;; 286 | esac -------------------------------------------------------------------------------- /onekey-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (C) 2021 Patrick⭐ 4 | # 5 | # 在 Linux 环境中一键安装 jd-base。 6 | # 7 | clear 8 | 9 | echo " 10 | _____ _____ ______ _ _______ 11 | (_____|____ \ (____ \ /\ | | (_______) 12 | _ _ \ \ ____) ) / \ \ \ _____ 13 | | | | | | | | __ ( / /\ \ \ \| ___) 14 | ___| | | |__/ / | |__) ) |__| |_____) ) |_____ 15 | (____/ |_____/ |______/|______(______/|_______) 16 | 17 | ================ Create by Patrick⭐ ================ 18 | " 19 | echo -e "\e[31m警告:请勿将本项目用于任何商业用途!\n\e[0m" 20 | 21 | ShellDir=$(cd "$(dirname "$0")";pwd) 22 | ShellName=$0 23 | JdDir=${ShellDir}/jd 24 | 25 | echo -e "\e[33m1.运行本脚本前必须自行安装好如下依赖:\e[0m" 26 | echo -e " git wget curl perl moreutils node.js npm\e[0m" 27 | echo -e "\e[33m2.安装过程请务必确保网络通畅\e[0m" 28 | echo -e "\e[33m\n按任意键开始安装,否则按 Ctrl + C 退出!\e[0m" 29 | read 30 | 31 | if [ ! -x "$(command -v node)" ] || [ ! -x "$(command -v npm)" ] || [ ! -x "$(command -v git)" ] || [ ! -x "$(command -v curl)" ] || [ ! -x "$(command -v wget)" ] || [ ! -x "$(command -v perl)" ]; then 32 | echo -e "\e[31m\n依赖未安装完整!\e[0m" 33 | exit 1 34 | fi 35 | 36 | echo -e "\n\e[32m1. 获取源码\e[0m" 37 | [ -d ${JdDir} ] && mv ${JdDir} ${JdDir}.bak && echo "检测到已有 ${JdDir} 目录,已备份为 ${JdDir}.bak" 38 | git clone -b v3 https://github.com.cnpmjs.org/RikudouPatrickstar/jd-base ${JdDir} 39 | 40 | echo -e "\n\e[32m2.1 如果有用于存放配置文件的远程 Git 仓库,请输入地址,否则直接回车:\e[0m" 41 | read remote_config 42 | [ -n "$remote_config" ] && git clone $remote_config ${JdDir}/config 43 | 44 | echo -e "\n\e[32m2.2 检查配置文件\e[0m" 45 | [ ! -d ${JdDir}/config ] && mkdir -p ${JdDir}/config 46 | 47 | if [ ! -s ${JdDir}/config/crontab.list ] 48 | then 49 | cp -fv ${JdDir}/sample/crontab.list.sample ${JdDir}/config/crontab.list 50 | sed -i "s,MY_PATH,${JdDir},g" ${JdDir}/config/crontab.list 51 | sed -i "s,ENV_PATH=,PATH=$PATH,g" ${JdDir}/config/crontab.list 52 | fi 53 | crond 54 | crontab -l > ${JdDir}/old_crontab 55 | crontab ${JdDir}/config/crontab.list 56 | 57 | [ ! -s ${JdDir}/config/config.sh ] && cp -fv ${JdDir}/sample/config.sh.sample ${JdDir}/config/config.sh 58 | [ ! -s ${JdDir}/config/auth.json ] && cp -fv ${JdDir}/sample/auth.json ${JdDir}/config/auth.json 59 | 60 | echo -e "\n\e[32m3. 执行 git_pull.sh\e[0m" 61 | bash ${JdDir}/git_pull.sh 62 | 63 | echo -e "\n\e[32m4. 启动控制面板\e[0m" 64 | cd ${JdDir}/panel >> /dev/null 65 | npm install --registry=https://mirrors.huaweicloud.com/repository/npm/ || npm install 66 | node server.js & 67 | cd ${ShellDir} 68 | echo -e "\e[32m请访问 http://:5678 进行配置\e[0m" 69 | echo -e "\e[32m初始用户名:admin,初始密码:password\e[0m" 70 | 71 | echo -e "\n更多关于控制面板的信息请访问:\n http://github.com/RikudouPatrickstar/jd-base#%E4%BA%94web-%E9%9D%A2%E6%9D%BF%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E \n" 72 | 73 | echo -e "\e[33m注意:原有定时任务已备份在 ${JdDir}/old_crontab \e[0m" 74 | rm -f ${ShellDir}/${ShellName} 75 | -------------------------------------------------------------------------------- /panel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "panel", 3 | "version": "1.0.0", 4 | "description": "web panel", 5 | "main": "server.js", 6 | "dependencies": { 7 | "body-parser": "^1.19.0", 8 | "compression": "^1.7.4", 9 | "express": "^4.17.1", 10 | "express-session": "^1.17.1", 11 | "got": "^11.8.1", 12 | "path": "^0.12.7" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /panel/public/auth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 京东脚本控制面板 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 23 | 24 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /panel/public/crontab.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 定时设定 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 | 34 |
35 |

在线编辑工具

36 | 37 |
38 |

crontab.list

39 |
40 | 41 |
42 |
43 |
44 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /panel/public/css/codemirror.min.css: -------------------------------------------------------------------------------- 1 | .CodeMirror{font-family:monospace;height:300px;color:black}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:white}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:black}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid black;border-right:0;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-ruler{border-left:1px solid #ccc;position:absolute}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:bold}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:red}.cm-invalidchar{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:white}.CodeMirror-scroll{overflow:scroll !important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;margin-bottom:-30px;*zoom:1;*display:inline}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none !important;border:none !important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper{-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-code{outline:0}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.CodeMirror span{*vertical-align:text-bottom}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0} -------------------------------------------------------------------------------- /panel/public/css/dracula.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Name: dracula 4 | Author: Michael Kaminsky (http://github.com/mkaminsky11) 5 | 6 | Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme) 7 | 8 | */ 9 | 10 | 11 | .cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters { 12 | background-color: #282a36 !important; 13 | color: #f8f8f2 !important; 14 | border: none; 15 | } 16 | .cm-s-dracula .CodeMirror-gutters { color: #282a36; } 17 | .cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; } 18 | .cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; } 19 | .cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } 20 | .cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); } 21 | .cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); } 22 | .cm-s-dracula span.cm-comment { color: #6272a4; } 23 | .cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; } 24 | .cm-s-dracula span.cm-number { color: #bd93f9; } 25 | .cm-s-dracula span.cm-variable { color: #50fa7b; } 26 | .cm-s-dracula span.cm-variable-2 { color: white; } 27 | .cm-s-dracula span.cm-def { color: #50fa7b; } 28 | .cm-s-dracula span.cm-operator { color: #ff79c6; } 29 | .cm-s-dracula span.cm-keyword { color: #ff79c6; } 30 | .cm-s-dracula span.cm-atom { color: #bd93f9; } 31 | .cm-s-dracula span.cm-meta { color: #f8f8f2; } 32 | .cm-s-dracula span.cm-tag { color: #ff79c6; } 33 | .cm-s-dracula span.cm-attribute { color: #50fa7b; } 34 | .cm-s-dracula span.cm-qualifier { color: #50fa7b; } 35 | .cm-s-dracula span.cm-property { color: #66d9ef; } 36 | .cm-s-dracula span.cm-builtin { color: #50fa7b; } 37 | .cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; } 38 | 39 | .cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); } 40 | .cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } 41 | -------------------------------------------------------------------------------- /panel/public/css/main.css: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | margin: 0; 4 | padding: 0; 5 | 6 | } 7 | 8 | h1, 9 | ul { 10 | margin: .3em 0 .3em 0; 11 | font-size: 1.5rem; 12 | padding-right: 1rem; 13 | } 14 | 15 | h4 { 16 | margin-top: 0.5em; 17 | } 18 | 19 | nav ul { 20 | margin: 0; 21 | padding: 0; 22 | } 23 | 24 | nav ul li { 25 | display: inline-block; 26 | } 27 | 28 | nav ul li a:link, 29 | nav ul li a:visited, 30 | nav ul li a:active { 31 | font-size: 0.875rem; 32 | padding: 0.5rem 0.8rem; 33 | color: white; 34 | text-decoration: none; 35 | background-color: #282a36; 36 | border-bottom-left-radius: 0.4rem; 37 | border-bottom-right-radius: 0.4rem; 38 | transition: background-color 0.3s; 39 | } 40 | 41 | nav ul li a:hover { 42 | background-color: #6D8A88; 43 | } 44 | 45 | nav .active a { 46 | background-color: #6D8A88 !important; 47 | } 48 | 49 | .container { 50 | position: relative; 51 | display: flex; 52 | flex-direction: column; 53 | height: 100vh; 54 | margin: 0 .5em; 55 | } 56 | 57 | .container p { 58 | font-size: 0.875rem; 59 | margin: 0; 60 | line-height: 1.5em; 61 | } 62 | 63 | .diffs { 64 | flex: 1 1 auto; 65 | display: flex; 66 | flex-direction: column; 67 | } 68 | 69 | .diffs header { 70 | margin-top: 1rem; 71 | } 72 | 73 | .diffs header * { 74 | display: inline-block; 75 | vertical-align: middle; 76 | } 77 | 78 | .diffs .compare-wrapper, 79 | .diffs .wrapper { 80 | flex: 1 1 auto; 81 | position: relative; 82 | } 83 | 84 | .diffs .compare-wrapper #compare { 85 | display: flex; 86 | flex-direction: column; 87 | position: absolute; 88 | top: 0; 89 | left: 0; 90 | bottom: 0; 91 | right: 0; 92 | } 93 | 94 | /* Auto-height fix */ 95 | 96 | .wrapper .CodeMirror { 97 | position: absolute; 98 | height: calc(100% - 0.5em); 99 | width: 100% 100 | } 101 | 102 | .mergely-column .CodeMirror { 103 | height: 100%; 104 | } 105 | 106 | .sources a { 107 | font-size: 1rem; 108 | } 109 | 110 | /* Login page */ 111 | .login-form { 112 | width: 300px; 113 | margin: 6rem auto 0 auto; 114 | font-family: Tahoma, Geneva, sans-serif; 115 | } 116 | 117 | .login-form h1 { 118 | text-align: center; 119 | color: #4d4d4d; 120 | font-size: 24px; 121 | padding: 20px 0 20px 0; 122 | } 123 | 124 | .login-form input[type="password"], 125 | .login-form input[type="text"] { 126 | width: 100%; 127 | padding: 15px; 128 | border: 1px solid #dddddd; 129 | margin-bottom: 15px; 130 | box-sizing: border-box; 131 | } 132 | 133 | .login-form input[type="submit"] { 134 | width: 100%; 135 | padding: 15px; 136 | background-color: #6D8A88; 137 | border: 0; 138 | box-sizing: border-box; 139 | cursor: pointer; 140 | font-weight: bold; 141 | color: #ffffff; 142 | } 143 | 144 | #qrcontainer { 145 | position: fixed; 146 | width: 100%; 147 | height: 100%; 148 | left: 0; 149 | top: 0; 150 | z-index: 1000; 151 | display: flex; 152 | align-items: center; 153 | justify-content: center; 154 | background: rgba(255, 255, 255, .8); 155 | text-align: center; 156 | transition: all 0.3s; 157 | } 158 | #qrcontainer.hidden{ 159 | opacity: 0; 160 | visibility: hidden; 161 | } 162 | 163 | #qrcontainer .qframe { 164 | background-color: #ffffff; 165 | padding: 1rem; 166 | border-radius: 0.5rem; 167 | border: #6D8A88 1px solid; 168 | -webkit-box-shadow: 0px 0px 7px 3px rgba(0, 0, 0, 0.2); 169 | box-shadow: 0px 0px 7px 3px rgba(0, 0, 0, 0.2); 170 | position: relative; 171 | } 172 | #qrcontainer .qframe #refresh_qrcode{ 173 | width: 256px; 174 | height: 256px; 175 | position: absolute; 176 | background-color: rgba(0, 0, 0, 0.5); 177 | left: 1rem; 178 | top: 1rem; 179 | color: #ffffff; 180 | display: flex; 181 | justify-content: center; 182 | align-items: center; 183 | } 184 | #qrcontainer .qframe #refresh_qrcode.hidden{ 185 | opacity: 0; 186 | visibility: hidden; 187 | } 188 | #qrcontainer .qframe #refresh_qrcode h3{ 189 | font-weight: normal; 190 | } 191 | #qrcontainer .qframe #refresh_qrcode .refresh{ 192 | display: block; 193 | background: #e4393c; 194 | width: 80px; 195 | height: 30px; 196 | margin: 0 auto; 197 | line-height: 30px; 198 | opacity: 1; 199 | z-index: 19; 200 | color: #fbfbfb; 201 | text-decoration: none; 202 | cursor: pointer; 203 | } 204 | #qrcontainer .qframe .info { 205 | padding: 1rem 0 0 0; 206 | } 207 | 208 | #qrcontainer .qframe .qframe-close { 209 | font-size: 20px; 210 | color: #f00; 211 | cursor: pointer; 212 | } 213 | 214 | @media screen and (max-width: 480px) { 215 | 216 | nav ul li a:link, 217 | nav ul li a:visited, 218 | nav ul li a:active { 219 | font-size: 0.75rem; 220 | padding: 0.5rem 0.5rem; 221 | margin: 0; 222 | 223 | } 224 | 225 | .container p { 226 | font-size: 0.75rem; 227 | margin: 0; 228 | line-height: 1.5em; 229 | } 230 | } 231 | 232 | .log-container { 233 | display: flex; 234 | flex-direction: column; 235 | height: 100vh; 236 | margin: 0 .5em; 237 | } 238 | @media (min-width: 992px) { 239 | .elk-app { 240 | height: 100%; 241 | } 242 | .elk-side { 243 | width: 20rem; 244 | } 245 | .elk-container, .elk-main, .elk-side { 246 | overflow-y: auto; 247 | } 248 | } 249 | .bg-light { 250 | background-color: #f8f9fa!important; 251 | } 252 | .left-list { 253 | overflow: auto; 254 | z-index: 100; 255 | } 256 | .text-dark { 257 | color: #343a40!important; 258 | } 259 | .nav-link { 260 | font-size: 18px; 261 | color: #007bff; 262 | text-decoration: none; 263 | background-color: transparent; 264 | } 265 | 266 | /* diff页面 */ 267 | .CodeMirror-merge { 268 | flex: 1; 269 | width: 100%; 270 | height: auto; 271 | display: flex; 272 | overflow: hidden; 273 | } 274 | .CodeMirror-merge .CodeMirror { 275 | height: 100%; 276 | } 277 | .CodeMirror-merge-2pane .CodeMirror-merge-pane { 278 | width: calc(50% - 15px); 279 | } 280 | .CodeMirror-merge-2pane .CodeMirror-merge-gap { 281 | width: 30px; 282 | } 283 | .CodeMirror-merge-2pane .CodeMirror-merge-scrolllock { 284 | display: none; 285 | } 286 | .CodeMirror-merge-pane-rightmost { 287 | position: static; 288 | } 289 | .CodeMirror-merge-r-chunk { background: #aaa; } 290 | .CodeMirror-merge-r-chunk-start { border-top: 1px solid #aaa; } 291 | .CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #aaa; } 292 | .CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; } 293 | 294 | 295 | 296 | /* 手动执行脚本页面 */ 297 | .run-cmd-form { 298 | margin-top: 8px; 299 | } 300 | 301 | .cmd-section { 302 | background: #282a36; 303 | color: #f8f8f2; 304 | padding: 0 8px; 305 | } 306 | 307 | .cmd-section input { 308 | width: 120px; 309 | } 310 | 311 | /* chrome 滚动条 */ 312 | ::-webkit-scrollbar-track-piece { 313 | width: 10px; 314 | background-color: rgba(140, 130, 130, .3); 315 | } 316 | 317 | ::-webkit-scrollbar { 318 | width: 10px; 319 | height: 10px; 320 | } 321 | 322 | ::-webkit-scrollbar-thumb { 323 | border-radius: 8px; 324 | background: rgba(177, 163, 163, .5); 325 | } 326 | 327 | ::-webkit-scrollbar-thumb:hover { 328 | background: rgba(177, 163, 163, .8); 329 | } 330 | -------------------------------------------------------------------------------- /panel/public/css/merge.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-merge { 2 | position: relative; 3 | border: 1px solid #ddd; 4 | white-space: pre; 5 | } 6 | 7 | .CodeMirror-merge, .CodeMirror-merge .CodeMirror { 8 | height: 350px; 9 | } 10 | 11 | .CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; } 12 | .CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; } 13 | .CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; } 14 | .CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; } 15 | 16 | .CodeMirror-merge-pane { 17 | display: inline-block; 18 | white-space: normal; 19 | vertical-align: top; 20 | } 21 | .CodeMirror-merge-pane-rightmost { 22 | position: absolute; 23 | right: 0px; 24 | z-index: 1; 25 | } 26 | 27 | .CodeMirror-merge-gap { 28 | z-index: 2; 29 | display: inline-block; 30 | height: 100%; 31 | -moz-box-sizing: border-box; 32 | box-sizing: border-box; 33 | overflow: hidden; 34 | border-left: 1px solid #ddd; 35 | border-right: 1px solid #ddd; 36 | position: relative; 37 | background: #f8f8f8; 38 | } 39 | 40 | .CodeMirror-merge-scrolllock-wrap { 41 | position: absolute; 42 | bottom: 0; left: 50%; 43 | } 44 | .CodeMirror-merge-scrolllock { 45 | position: relative; 46 | left: -50%; 47 | cursor: pointer; 48 | color: #555; 49 | line-height: 1; 50 | } 51 | .CodeMirror-merge-scrolllock:after { 52 | content: "\21db\00a0\00a0\21da"; 53 | } 54 | .CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled:after { 55 | content: "\21db\21da"; 56 | } 57 | 58 | .CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right { 59 | position: absolute; 60 | left: 0; top: 0; 61 | right: 0; bottom: 0; 62 | line-height: 1; 63 | } 64 | 65 | .CodeMirror-merge-copy { 66 | position: absolute; 67 | cursor: pointer; 68 | color: #44c; 69 | z-index: 3; 70 | } 71 | 72 | .CodeMirror-merge-copy-reverse { 73 | position: absolute; 74 | cursor: pointer; 75 | color: #44c; 76 | } 77 | 78 | .CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; } 79 | .CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; } 80 | 81 | .CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted { 82 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==); 83 | background-position: bottom left; 84 | background-repeat: repeat-x; 85 | } 86 | 87 | .CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted { 88 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==); 89 | background-position: bottom left; 90 | background-repeat: repeat-x; 91 | } 92 | 93 | .CodeMirror-merge-r-chunk { background: #ffffe0; } 94 | .CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; } 95 | .CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; } 96 | .CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; } 97 | 98 | .CodeMirror-merge-l-chunk { background: #eef; } 99 | .CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; } 100 | .CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; } 101 | .CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; } 102 | 103 | .CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; } 104 | .CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; } 105 | .CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; } 106 | 107 | .CodeMirror-merge-collapsed-widget:before { 108 | content: "(...)"; 109 | } 110 | .CodeMirror-merge-collapsed-widget { 111 | cursor: pointer; 112 | color: #88b; 113 | background: #eef; 114 | border: 1px solid #ddf; 115 | font-size: 90%; 116 | padding: 0 3px; 117 | border-radius: 4px; 118 | } 119 | .CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; } 120 | -------------------------------------------------------------------------------- /panel/public/css/twilight.css: -------------------------------------------------------------------------------- 1 | .cm-s-twilight.CodeMirror { background: #141414; color: #f7f7f7; } /**/ 2 | .cm-s-twilight div.CodeMirror-selected { background: #323232; } /**/ 3 | .cm-s-twilight .CodeMirror-line::selection, .cm-s-twilight .CodeMirror-line > span::selection, .cm-s-twilight .CodeMirror-line > span > span::selection { background: rgba(50, 50, 50, 0.99); } 4 | .cm-s-twilight .CodeMirror-line::-moz-selection, .cm-s-twilight .CodeMirror-line > span::-moz-selection, .cm-s-twilight .CodeMirror-line > span > span::-moz-selection { background: rgba(50, 50, 50, 0.99); } 5 | 6 | .cm-s-twilight .CodeMirror-gutters { background: #222; border-right: 1px solid #aaa; } 7 | .cm-s-twilight .CodeMirror-guttermarker { color: white; } 8 | .cm-s-twilight .CodeMirror-guttermarker-subtle { color: #aaa; } 9 | .cm-s-twilight .CodeMirror-linenumber { color: #aaa; } 10 | .cm-s-twilight .CodeMirror-cursor { border-left: 1px solid white; } 11 | 12 | .cm-s-twilight .cm-keyword { color: #f9ee98; } /**/ 13 | .cm-s-twilight .cm-atom { color: #FC0; } 14 | .cm-s-twilight .cm-number { color: #ca7841; } /**/ 15 | .cm-s-twilight .cm-def { color: #8DA6CE; } 16 | .cm-s-twilight span.cm-variable-2, .cm-s-twilight span.cm-tag { color: #607392; } /**/ 17 | .cm-s-twilight span.cm-variable-3, .cm-s-twilight span.cm-def, .cm-s-twilight span.cm-type { color: #607392; } /**/ 18 | .cm-s-twilight .cm-operator { color: #cda869; } /**/ 19 | .cm-s-twilight .cm-comment { color:#777; font-style:italic; font-weight:normal; } /**/ 20 | .cm-s-twilight .cm-string { color:#8f9d6a; font-style:italic; } /**/ 21 | .cm-s-twilight .cm-string-2 { color:#bd6b18; } /*?*/ 22 | .cm-s-twilight .cm-meta { background-color:#141414; color:#f7f7f7; } /*?*/ 23 | .cm-s-twilight .cm-builtin { color: #cda869; } /*?*/ 24 | .cm-s-twilight .cm-tag { color: #997643; } /**/ 25 | .cm-s-twilight .cm-attribute { color: #d6bb6d; } /*?*/ 26 | .cm-s-twilight .cm-header { color: #FF6400; } 27 | .cm-s-twilight .cm-hr { color: #AEAEAE; } 28 | .cm-s-twilight .cm-link { color:#ad9361; font-style:italic; text-decoration:none; } /**/ 29 | .cm-s-twilight .cm-error { border-bottom: 1px solid red; } 30 | 31 | .cm-s-twilight .CodeMirror-activeline-background { background: #27282E; } 32 | .cm-s-twilight .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; } 33 | -------------------------------------------------------------------------------- /panel/public/diff.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 对比工具 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 36 |
37 |

在线对比合并工具

38 | 39 | 40 | 41 | 42 |
43 |

左侧为 config.sh ,右侧为 config.sh.sample

44 |
45 |
46 |
47 |
48 |
49 |

查看源文件,注意将浏览器编码切换为 UTF-8 。

50 | 54 |
55 |
56 |
57 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /panel/public/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 参数配置 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 35 |
36 |

在线编辑工具

37 | 38 | 39 | 40 |    41 | 42 | 43 | 44 | 45 |
46 |

config.sh

47 |
48 | 49 |
50 |
51 |
52 | 65 | 223 | 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /panel/public/js/comment.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var noOptions = {}; 15 | var nonWS = /[^\s\u00a0]/; 16 | var Pos = CodeMirror.Pos, cmp = CodeMirror.cmpPos; 17 | 18 | function firstNonWS(str) { 19 | var found = str.search(nonWS); 20 | return found == -1 ? 0 : found; 21 | } 22 | 23 | CodeMirror.commands.toggleComment = function(cm) { 24 | cm.toggleComment(); 25 | }; 26 | 27 | CodeMirror.defineExtension("toggleComment", function(options) { 28 | if (!options) options = noOptions; 29 | var cm = this; 30 | var minLine = Infinity, ranges = this.listSelections(), mode = null; 31 | for (var i = ranges.length - 1; i >= 0; i--) { 32 | var from = ranges[i].from(), to = ranges[i].to(); 33 | if (from.line >= minLine) continue; 34 | if (to.line >= minLine) to = Pos(minLine, 0); 35 | minLine = from.line; 36 | if (mode == null) { 37 | if (cm.uncomment(from, to, options)) mode = "un"; 38 | else { cm.lineComment(from, to, options); mode = "line"; } 39 | } else if (mode == "un") { 40 | cm.uncomment(from, to, options); 41 | } else { 42 | cm.lineComment(from, to, options); 43 | } 44 | } 45 | }); 46 | 47 | // Rough heuristic to try and detect lines that are part of multi-line string 48 | function probablyInsideString(cm, pos, line) { 49 | return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line) 50 | } 51 | 52 | function getMode(cm, pos) { 53 | var mode = cm.getMode() 54 | return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos) 55 | } 56 | 57 | CodeMirror.defineExtension("lineComment", function(from, to, options) { 58 | if (!options) options = noOptions; 59 | var self = this, mode = getMode(self, from); 60 | var firstLine = self.getLine(from.line); 61 | if (firstLine == null || probablyInsideString(self, from, firstLine)) return; 62 | 63 | var commentString = options.lineComment || mode.lineComment; 64 | if (!commentString) { 65 | if (options.blockCommentStart || mode.blockCommentStart) { 66 | options.fullLines = true; 67 | self.blockComment(from, to, options); 68 | } 69 | return; 70 | } 71 | 72 | var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); 73 | var pad = options.padding == null ? " " : options.padding; 74 | var blankLines = options.commentBlankLines || from.line == to.line; 75 | 76 | self.operation(function() { 77 | if (options.indent) { 78 | var baseString = null; 79 | for (var i = from.line; i < end; ++i) { 80 | var line = self.getLine(i); 81 | var whitespace = line.slice(0, firstNonWS(line)); 82 | if (baseString == null || baseString.length > whitespace.length) { 83 | baseString = whitespace; 84 | } 85 | } 86 | for (var i = from.line; i < end; ++i) { 87 | var line = self.getLine(i), cut = baseString.length; 88 | if (!blankLines && !nonWS.test(line)) continue; 89 | if (line.slice(0, cut) != baseString) cut = firstNonWS(line); 90 | self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut)); 91 | } 92 | } else { 93 | for (var i = from.line; i < end; ++i) { 94 | if (blankLines || nonWS.test(self.getLine(i))) 95 | self.replaceRange(commentString + pad, Pos(i, 0)); 96 | } 97 | } 98 | }); 99 | }); 100 | 101 | CodeMirror.defineExtension("blockComment", function(from, to, options) { 102 | if (!options) options = noOptions; 103 | var self = this, mode = getMode(self, from); 104 | var startString = options.blockCommentStart || mode.blockCommentStart; 105 | var endString = options.blockCommentEnd || mode.blockCommentEnd; 106 | if (!startString || !endString) { 107 | if ((options.lineComment || mode.lineComment) && options.fullLines != false) 108 | self.lineComment(from, to, options); 109 | return; 110 | } 111 | if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return 112 | 113 | var end = Math.min(to.line, self.lastLine()); 114 | if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end; 115 | 116 | var pad = options.padding == null ? " " : options.padding; 117 | if (from.line > end) return; 118 | 119 | self.operation(function() { 120 | if (options.fullLines != false) { 121 | var lastLineHasText = nonWS.test(self.getLine(end)); 122 | self.replaceRange(pad + endString, Pos(end)); 123 | self.replaceRange(startString + pad, Pos(from.line, 0)); 124 | var lead = options.blockCommentLead || mode.blockCommentLead; 125 | if (lead != null) for (var i = from.line + 1; i <= end; ++i) 126 | if (i != end || lastLineHasText) 127 | self.replaceRange(lead + pad, Pos(i, 0)); 128 | } else { 129 | var atCursor = cmp(self.getCursor("to"), to) == 0, empty = !self.somethingSelected() 130 | self.replaceRange(endString, to); 131 | if (atCursor) self.setSelection(empty ? to : self.getCursor("from"), to) 132 | self.replaceRange(startString, from); 133 | } 134 | }); 135 | }); 136 | 137 | CodeMirror.defineExtension("uncomment", function(from, to, options) { 138 | if (!options) options = noOptions; 139 | var self = this, mode = getMode(self, from); 140 | var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end); 141 | 142 | // Try finding line comments 143 | var lineString = options.lineComment || mode.lineComment, lines = []; 144 | var pad = options.padding == null ? " " : options.padding, didSomething; 145 | lineComment: { 146 | if (!lineString) break lineComment; 147 | for (var i = start; i <= end; ++i) { 148 | var line = self.getLine(i); 149 | var found = line.indexOf(lineString); 150 | if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; 151 | if (found == -1 && nonWS.test(line)) break lineComment; 152 | if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; 153 | lines.push(line); 154 | } 155 | self.operation(function() { 156 | for (var i = start; i <= end; ++i) { 157 | var line = lines[i - start]; 158 | var pos = line.indexOf(lineString), endPos = pos + lineString.length; 159 | if (pos < 0) continue; 160 | if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length; 161 | didSomething = true; 162 | self.replaceRange("", Pos(i, pos), Pos(i, endPos)); 163 | } 164 | }); 165 | if (didSomething) return true; 166 | } 167 | 168 | // Try block comments 169 | var startString = options.blockCommentStart || mode.blockCommentStart; 170 | var endString = options.blockCommentEnd || mode.blockCommentEnd; 171 | if (!startString || !endString) return false; 172 | var lead = options.blockCommentLead || mode.blockCommentLead; 173 | var startLine = self.getLine(start), open = startLine.indexOf(startString) 174 | if (open == -1) return false 175 | var endLine = end == start ? startLine : self.getLine(end) 176 | var close = endLine.indexOf(endString, end == start ? open + startString.length : 0); 177 | var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1) 178 | if (close == -1 || 179 | !/comment/.test(self.getTokenTypeAt(insideStart)) || 180 | !/comment/.test(self.getTokenTypeAt(insideEnd)) || 181 | self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1) 182 | return false; 183 | 184 | // Avoid killing block comments completely outside the selection. 185 | // Positions of the last startString before the start of the selection, and the first endString after it. 186 | var lastStart = startLine.lastIndexOf(startString, from.ch); 187 | var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length); 188 | if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false; 189 | // Positions of the first endString after the end of the selection, and the last startString before it. 190 | firstEnd = endLine.indexOf(endString, to.ch); 191 | var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch); 192 | lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart; 193 | if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false; 194 | 195 | self.operation(function() { 196 | self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), 197 | Pos(end, close + endString.length)); 198 | var openEnd = open + startString.length; 199 | if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length; 200 | self.replaceRange("", Pos(start, open), Pos(start, openEnd)); 201 | if (lead) for (var i = start + 1; i <= end; ++i) { 202 | var line = self.getLine(i), found = line.indexOf(lead); 203 | if (found == -1 || nonWS.test(line.slice(0, found))) continue; 204 | var foundEnd = found + lead.length; 205 | if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length; 206 | self.replaceRange("", Pos(i, found), Pos(i, foundEnd)); 207 | } 208 | }); 209 | return true; 210 | }); 211 | }); 212 | -------------------------------------------------------------------------------- /panel/public/js/diff_match_patch.js: -------------------------------------------------------------------------------- 1 | var diff_match_patch=function(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=.5;this.Patch_Margin=4;this.Match_MaxBits=32},DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0;diff_match_patch.Diff=function(a,b){this[0]=a;this[1]=b};diff_match_patch.Diff.prototype.length=2;diff_match_patch.Diff.prototype.toString=function(){return this[0]+","+this[1]}; 2 | diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[new diff_match_patch.Diff(DIFF_EQUAL,a)]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);f=this.diff_commonSuffix(a,b);var g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0, 3 | b.length-f);a=this.diff_compute_(a,b,e,d);c&&a.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c));g&&a.push(new diff_match_patch.Diff(DIFF_EQUAL,g));this.diff_cleanupMerge(a);return a}; 4 | diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[new diff_match_patch.Diff(DIFF_INSERT,b)];if(!b)return[new diff_match_patch.Diff(DIFF_DELETE,a)];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[new diff_match_patch.Diff(DIFF_INSERT,e.substring(0,g)),new diff_match_patch.Diff(DIFF_EQUAL,f),new diff_match_patch.Diff(DIFF_INSERT,e.substring(g+f.length))],a.length>b.length&&(c[0][0]=c[2][0]=DIFF_DELETE),c):1==f.length?[new diff_match_patch.Diff(DIFF_DELETE, 5 | a),new diff_match_patch.Diff(DIFF_INSERT,b)]:(e=this.diff_halfMatch_(a,b))?(b=e[1],f=e[3],a=e[4],e=this.diff_main(e[0],e[2],c,d),c=this.diff_main(b,f,c,d),e.concat([new diff_match_patch.Diff(DIFF_EQUAL,a)],c)):c&&100c);t++){for(var v=-t+p;v<=t-x;v+=2){var n=f+v;var r=v==-t||v!=t&&h[n-1]d)x+=2;else if(y>e)p+=2;else if(m&&(n=f+k-v,0<=n&&n= 9 | u)return this.diff_bisectSplit_(a,b,r,y,c)}}for(v=-t+w;v<=t-q;v+=2){n=f+v;u=v==-t||v!=t&&l[n-1]d)q+=2;else if(r>e)w+=2;else if(!m&&(n=f+k-v,0<=n&&n=u)))return this.diff_bisectSplit_(a,b,r,y,c)}}return[new diff_match_patch.Diff(DIFF_DELETE,a),new diff_match_patch.Diff(DIFF_INSERT,b)]}; 10 | diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)}; 11 | diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,g=-1,h=d.length;gd?a=a.substring(c-d):c=a.length?[h,k,l,m,g]:null}if(0>=this.Diff_Timeout)return null; 16 | var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.lengthd[4].length?g:d:d:g;else return null;if(a.length>b.length){d=g[0];e=g[1];var h=g[2];var l=g[3]}else h=g[0],l=g[1],d=g[2],e=g[3];return[d,e,h,l,g[4]]}; 17 | diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,l=0,k=0;f=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,c.substring(0,d))),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,b.substring(0,e))),a[f-1][0]=DIFF_INSERT,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=DIFF_DELETE, 19 | a[f+1][1]=b.substring(e),f++;f++}f++}}; 20 | diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_);c=g&&c.match(diff_match_patch.linebreakRegex_);d=h&&d.match(diff_match_patch.linebreakRegex_);var k=c&&a.match(diff_match_patch.blanklineEndRegex_),l=d&&b.match(diff_match_patch.blanklineStartRegex_); 21 | return k||l?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c=k&&(k=m,g=d,h=e,l=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c- 22 | 1,1),c--),a[c][1]=h,l?a[c+1][1]=l:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/; 23 | diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,l=!1,k=!1;fb)break;e=c;f=d}return a.length!=g&&a[g][0]===DIFF_DELETE?f:f+(b-e)}; 28 | diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=//g,f=/\n/g,g=0;g");switch(h){case DIFF_INSERT:b[g]=''+l+"";break;case DIFF_DELETE:b[g]=''+l+"";break;case DIFF_EQUAL:b[g]=""+l+""}}return b.join("")}; 29 | diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;cthis.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));var l=1<=k;q--){var t=e[a.charAt(q-1)];m[q]=0===w?(m[q+1]<<1|1)&t:(m[q+1]<<1|1)&t|(x[q+1]|x[q])<<1|1|x[q+1];if(m[q]&l&&(t=d(w,q-1),t<=g))if(g=t,h=q-1,h>c)k=Math.max(1,2*c-h);else break}if(d(w+1,c)>g)break;x=m}return h}; 36 | diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c=2*this.Patch_Margin&&e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}k!==DIFF_INSERT&&(f+=m.length);k!==DIFF_DELETE&&(g+=m.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c}; 42 | diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;cthis.Match_MaxBits){var k=this.match_main(b,h.substring(0,this.Match_MaxBits),g);-1!=k&&(l=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==l||k>=l)&&(k=-1)}else k=this.match_main(b,h, 44 | g);if(-1==k)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=k-g,g=-1==l?b.substring(k,k+h.length):b.substring(k,l+this.Match_MaxBits),h==g)b=b.substring(0,k)+this.diff_text2(a[f].diffs)+b.substring(k+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);h=0;var m;for(l=0;le[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs; 47 | 0==e.length||e[e.length-1][0]!=DIFF_EQUAL?(e.push(new diff_match_patch.Diff(DIFF_EQUAL,c)),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c}; 48 | diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c2*b?(h.length1+=k.length,e+=k.length,l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),d.diffs.shift()):(k=k.substring(0,b-h.length1-this.Patch_Margin),h.length1+=k.length,e+=k.length,g===DIFF_EQUAL?(h.length2+=k.length,f+=k.length):l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),k==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(k.length))}g=this.diff_text2(h.diffs); 50 | g=g.substring(g.length-this.Patch_Margin);k=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==k&&(h.length1+=k.length,h.length2+=k.length,0!==h.diffs.length&&h.diffs[h.diffs.length-1][0]===DIFF_EQUAL?h.diffs[h.diffs.length-1][1]+=k:h.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,k)));l||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;cd;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j=0?p.get(q):0}}for(var r=0,m=0;mm;m++)for(var j=0;jm;m++)for(var j=0;j=0;)b^=f.G15<=0;)b^=f.G18<>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;cf;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=[''],h=0;d>h;h++){g.push("");for(var i=0;d>i;i++)g.push('');g.push("")}g.push("
"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}(); -------------------------------------------------------------------------------- /panel/public/js/shell.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineMode('shell', function() { 15 | 16 | var words = {}; 17 | function define(style, dict) { 18 | for(var i = 0; i < dict.length; i++) { 19 | words[dict[i]] = style; 20 | } 21 | }; 22 | 23 | var commonAtoms = ["true", "false"]; 24 | var commonKeywords = ["if", "then", "do", "else", "elif", "while", "until", "for", "in", "esac", "fi", 25 | "fin", "fil", "done", "exit", "set", "unset", "export", "function"]; 26 | var commonCommands = ["ab", "awk", "bash", "beep", "cat", "cc", "cd", "chown", "chmod", "chroot", "clear", 27 | "cp", "curl", "cut", "diff", "echo", "find", "gawk", "gcc", "get", "git", "grep", "hg", "kill", "killall", 28 | "ln", "ls", "make", "mkdir", "openssl", "mv", "nc", "nl", "node", "npm", "ping", "ps", "restart", "rm", 29 | "rmdir", "sed", "service", "sh", "shopt", "shred", "source", "sort", "sleep", "ssh", "start", "stop", 30 | "su", "sudo", "svn", "tee", "telnet", "top", "touch", "vi", "vim", "wall", "wc", "wget", "who", "write", 31 | "yes", "zsh"]; 32 | 33 | CodeMirror.registerHelper("hintWords", "shell", commonAtoms.concat(commonKeywords, commonCommands)); 34 | 35 | define('atom', commonAtoms); 36 | define('keyword', commonKeywords); 37 | define('builtin', commonCommands); 38 | 39 | function tokenBase(stream, state) { 40 | if (stream.eatSpace()) return null; 41 | 42 | var sol = stream.sol(); 43 | var ch = stream.next(); 44 | 45 | if (ch === '\\') { 46 | stream.next(); 47 | return null; 48 | } 49 | if (ch === '\'' || ch === '"' || ch === '`') { 50 | state.tokens.unshift(tokenString(ch, ch === "`" ? "quote" : "string")); 51 | return tokenize(stream, state); 52 | } 53 | if (ch === '#') { 54 | if (sol && stream.eat('!')) { 55 | stream.skipToEnd(); 56 | return 'meta'; // 'comment'? 57 | } 58 | stream.skipToEnd(); 59 | return 'comment'; 60 | } 61 | if (ch === '$') { 62 | state.tokens.unshift(tokenDollar); 63 | return tokenize(stream, state); 64 | } 65 | if (ch === '+' || ch === '=') { 66 | return 'operator'; 67 | } 68 | if (ch === '-') { 69 | stream.eat('-'); 70 | stream.eatWhile(/\w/); 71 | return 'attribute'; 72 | } 73 | if (ch == "<") { 74 | if (stream.match("<<")) return "operator" 75 | var heredoc = stream.match(/^<-?\s*['"]?([^'"]*)['"]?/) 76 | if (heredoc) { 77 | state.tokens.unshift(tokenHeredoc(heredoc[1])) 78 | return 'string-2' 79 | } 80 | } 81 | if (/\d/.test(ch)) { 82 | stream.eatWhile(/\d/); 83 | if(stream.eol() || !/\w/.test(stream.peek())) { 84 | return 'number'; 85 | } 86 | } 87 | stream.eatWhile(/[\w-]/); 88 | var cur = stream.current(); 89 | if (stream.peek() === '=' && /\w+/.test(cur)) return 'def'; 90 | return words.hasOwnProperty(cur) ? words[cur] : null; 91 | } 92 | 93 | function tokenString(quote, style) { 94 | var close = quote == "(" ? ")" : quote == "{" ? "}" : quote 95 | return function(stream, state) { 96 | var next, escaped = false; 97 | while ((next = stream.next()) != null) { 98 | if (next === close && !escaped) { 99 | state.tokens.shift(); 100 | break; 101 | } else if (next === '$' && !escaped && quote !== "'" && stream.peek() != close) { 102 | escaped = true; 103 | stream.backUp(1); 104 | state.tokens.unshift(tokenDollar); 105 | break; 106 | } else if (!escaped && quote !== close && next === quote) { 107 | state.tokens.unshift(tokenString(quote, style)) 108 | return tokenize(stream, state) 109 | } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) { 110 | state.tokens.unshift(tokenStringStart(next, "string")); 111 | stream.backUp(1); 112 | break; 113 | } 114 | escaped = !escaped && next === '\\'; 115 | } 116 | return style; 117 | }; 118 | }; 119 | 120 | function tokenStringStart(quote, style) { 121 | return function(stream, state) { 122 | state.tokens[0] = tokenString(quote, style) 123 | stream.next() 124 | return tokenize(stream, state) 125 | } 126 | } 127 | 128 | var tokenDollar = function(stream, state) { 129 | if (state.tokens.length > 1) stream.eat('$'); 130 | var ch = stream.next() 131 | if (/['"({]/.test(ch)) { 132 | state.tokens[0] = tokenString(ch, ch == "(" ? "quote" : ch == "{" ? "def" : "string"); 133 | return tokenize(stream, state); 134 | } 135 | if (!/\d/.test(ch)) stream.eatWhile(/\w/); 136 | state.tokens.shift(); 137 | return 'def'; 138 | }; 139 | 140 | function tokenHeredoc(delim) { 141 | return function(stream, state) { 142 | if (stream.sol() && stream.string == delim) state.tokens.shift() 143 | stream.skipToEnd() 144 | return "string-2" 145 | } 146 | } 147 | 148 | function tokenize(stream, state) { 149 | return (state.tokens[0] || tokenBase) (stream, state); 150 | }; 151 | 152 | return { 153 | startState: function() {return {tokens:[]};}, 154 | token: function(stream, state) { 155 | return tokenize(stream, state); 156 | }, 157 | closeBrackets: "()[]{}''\"\"``", 158 | lineComment: '#', 159 | fold: "brace" 160 | }; 161 | }); 162 | 163 | CodeMirror.defineMIME('text/x-sh', 'shell'); 164 | // Apache uses a slightly different Media Type for Shell scripts 165 | // http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types 166 | CodeMirror.defineMIME('application/x-sh', 'shell'); 167 | 168 | }); 169 | -------------------------------------------------------------------------------- /panel/public/pwd.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 修改用户名密码 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 27 | 35 |
36 |
37 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /panel/public/run.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 手动执行脚本 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 | 32 |
33 |

手动执行脚本

34 | 35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 |
46 |
47 | bash jd.sh now 48 |
49 | 50 |
51 |
52 |

运行日志

53 |
54 | 55 |
56 |
57 |
58 | 246 | 247 | 248 | 249 | 250 | -------------------------------------------------------------------------------- /panel/public/tasklog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 日志查询 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |
23 |
24 | 35 |
36 | 37 |

日志查询

38 | 39 |
40 |
41 | 42 |
43 | 54 |
55 |
56 | 57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /panel/server.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * @Author: Jerrykuku https://github.com/jerrykuku 4 | * @Date: 2021-1-8 5 | * @Version: v0.0.2 6 | * @thanks: FanchangWang https://github.com/FanchangWang 7 | */ 8 | 9 | var express = require('express'); 10 | var session = require('express-session'); 11 | var compression = require('compression'); 12 | var bodyParser = require('body-parser'); 13 | var got = require('got'); 14 | var path = require('path'); 15 | var fs = require('fs'); 16 | var { execSync, exec } = require('child_process'); 17 | 18 | var rootPath = path.resolve(__dirname, '..') 19 | // config.sh 文件所在目录 20 | var confFile = path.join(rootPath, 'config/config.sh'); 21 | // config.sh.sample 文件所在目录 22 | var sampleFile = path.join(rootPath, 'sample/config.sh.sample'); 23 | // crontab.list 文件所在目录 24 | var crontabFile = path.join(rootPath, 'config/crontab.list'); 25 | // config.sh 文件备份目录 26 | var confBakDir = path.join(rootPath, 'config/bak/'); 27 | // auth.json 文件目录 28 | var authConfigFile = path.join(rootPath, 'config/auth.json'); 29 | // 日志目录 30 | var logPath = path.join(rootPath, 'log/'); 31 | // 脚本目录 32 | var ScriptsPath = path.join(rootPath, 'scripts/'); 33 | 34 | var authError = "错误的用户名密码,请重试"; 35 | var loginFaild = "请先登录!"; 36 | 37 | var configString = "config sample crontab"; 38 | 39 | var s_token, cookies, guid, lsid, lstoken, okl_token, token, userCookie = "" 40 | 41 | function praseSetCookies(response) { 42 | s_token = response.body.s_token 43 | guid = response.headers['set-cookie'][0] 44 | guid = guid.substring(guid.indexOf("=") + 1, guid.indexOf(";")) 45 | lsid = response.headers['set-cookie'][2] 46 | lsid = lsid.substring(lsid.indexOf("=") + 1, lsid.indexOf(";")) 47 | lstoken = response.headers['set-cookie'][3] 48 | lstoken = lstoken.substring(lstoken.indexOf("=") + 1, lstoken.indexOf(";")) 49 | cookies = "guid=" + guid + "; lang=chs; lsid=" + lsid + "; lstoken=" + lstoken + "; " 50 | } 51 | 52 | function getCookie(response) { 53 | var TrackerID = response.headers['set-cookie'][0] 54 | TrackerID = TrackerID.substring(TrackerID.indexOf("=") + 1, TrackerID.indexOf(";")) 55 | var pt_key = response.headers['set-cookie'][1] 56 | pt_key = pt_key.substring(pt_key.indexOf("=") + 1, pt_key.indexOf(";")) 57 | var pt_pin = response.headers['set-cookie'][2] 58 | pt_pin = pt_pin.substring(pt_pin.indexOf("=") + 1, pt_pin.indexOf(";")) 59 | var pt_token = response.headers['set-cookie'][3] 60 | pt_token = pt_token.substring(pt_token.indexOf("=") + 1, pt_token.indexOf(";")) 61 | var pwdt_id = response.headers['set-cookie'][4] 62 | pwdt_id = pwdt_id.substring(pwdt_id.indexOf("=") + 1, pwdt_id.indexOf(";")) 63 | var s_key = response.headers['set-cookie'][5] 64 | s_key = s_key.substring(s_key.indexOf("=") + 1, s_key.indexOf(";")) 65 | var s_pin = response.headers['set-cookie'][6] 66 | s_pin = s_pin.substring(s_pin.indexOf("=") + 1, s_pin.indexOf(";")) 67 | cookies = "TrackerID=" + TrackerID + "; pt_key=" + pt_key + "; pt_pin=" + pt_pin + "; pt_token=" + pt_token + "; pwdt_id=" + pwdt_id + "; s_key=" + s_key + "; s_pin=" + s_pin + "; wq_skey=" 68 | var userCookie = "pt_key=" + pt_key + ";pt_pin=" + pt_pin + ";"; 69 | console.log("\n############ 登录成功,获取到 Cookie #############\n\n"); 70 | console.log('Cookie1="' + userCookie + '"\n'); 71 | console.log("\n####################################################\n\n"); 72 | return userCookie; 73 | } 74 | 75 | async function step1() { 76 | try { 77 | s_token, cookies, guid, lsid, lstoken, okl_token, token = "" 78 | let timeStamp = (new Date()).getTime() 79 | let url = 'https://plogin.m.jd.com/cgi-bin/mm/new_login_entrance?lang=chs&appid=300&returnurl=https://wq.jd.com/passport/LoginRedirect?state=' + timeStamp + '&returnurl=https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport' 80 | const response = await got(url, { 81 | responseType: 'json', 82 | headers: { 83 | 'Connection': 'Keep-Alive', 84 | 'Content-Type': 'application/x-www-form-urlencoded', 85 | 'Accept': 'application/json, text/plain, */*', 86 | 'Accept-Language': 'zh-cn', 87 | 'Referer': 'https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wq.jd.com/passport/LoginRedirect?state=' + timeStamp + '&returnurl=https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport', 88 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36', 89 | 'Host': 'plogin.m.jd.com' 90 | } 91 | }); 92 | 93 | praseSetCookies(response) 94 | } catch (error) { 95 | cookies = ""; 96 | console.log(error.response.body); 97 | } 98 | }; 99 | 100 | async function step2() { 101 | try { 102 | if (cookies == "") { 103 | return 0 104 | } 105 | let timeStamp = (new Date()).getTime() 106 | let url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthreflogurl?s_token=' + s_token + '&v=' + timeStamp + '&remember=true' 107 | const response = await got.post(url, { 108 | responseType: 'json', 109 | json: { 110 | 'lang': 'chs', 111 | 'appid': 300, 112 | 'returnurl': 'https://wqlogin2.jd.com/passport/LoginRedirect?state=' + timeStamp + '&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action', 113 | 'source': 'wq_passport' 114 | }, 115 | headers: { 116 | 'Connection': 'Keep-Alive', 117 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 118 | 'Accept': 'application/json, text/plain, */*', 119 | 'Cookie': cookies, 120 | 'Referer': 'https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=' + timeStamp + '&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport', 121 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36', 122 | 'Host': 'plogin.m.jd.com', 123 | } 124 | }); 125 | token = response.body.token 126 | okl_token = response.headers['set-cookie'][0] 127 | okl_token = okl_token.substring(okl_token.indexOf("=") + 1, okl_token.indexOf(";")) 128 | var qrUrl = 'https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token=' + token; 129 | return qrUrl; 130 | } catch (error) { 131 | console.log(error.response.body); 132 | return 0 133 | } 134 | } 135 | 136 | var i = 0; 137 | 138 | async function checkLogin() { 139 | try { 140 | if (cookies == "") { 141 | return 0 142 | } 143 | let timeStamp = (new Date()).getTime() 144 | let url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthchecktoken?&token=' + token + '&ou_state=0&okl_token=' + okl_token; 145 | const response = await got.post(url, { 146 | responseType: 'json', 147 | form: { 148 | lang: 'chs', 149 | appid: 300, 150 | returnurl: 'https://wqlogin2.jd.com/passport/LoginRedirect?state=1100399130787&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action', 151 | source: 'wq_passport' 152 | }, 153 | headers: { 154 | 'Referer': 'https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=' + timeStamp + '&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport', 155 | 'Cookie': cookies, 156 | 'Connection': 'Keep-Alive', 157 | 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', 158 | 'Accept': 'application/json, text/plain, */*', 159 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36', 160 | } 161 | }); 162 | 163 | return response; 164 | } catch (error) { 165 | console.log(error.response.body); 166 | let res = {} 167 | res.body = { check_ip: 0, errcode: 222, message: '出错' } 168 | res.headers = {} 169 | return res; 170 | } 171 | } 172 | 173 | 174 | 175 | /** 176 | * 检查 config.sh 以及 config.sh.sample 文件是否存在 177 | */ 178 | function checkConfigFile() { 179 | if (!fs.existsSync(confFile)) { 180 | console.error('脚本启动失败,config.sh 文件不存在!'); 181 | process.exit(1); 182 | } 183 | if (!fs.existsSync(sampleFile)) { 184 | console.error('脚本启动失败,config.sh.sample 文件不存在!'); 185 | process.exit(1); 186 | } 187 | } 188 | 189 | /** 190 | * 检查 config/bak/ 备份目录是否存在,不存在则创建 191 | */ 192 | function mkdirConfigBakDir() { 193 | if (!fs.existsSync(confBakDir)) { 194 | fs.mkdirSync(confBakDir); 195 | } 196 | } 197 | 198 | /** 199 | * 备份 config.sh 文件 200 | */ 201 | function bakConfFile(file) { 202 | mkdirConfigBakDir(); 203 | let date = new Date(); 204 | let bakConfFile = confBakDir + file + '_' + date.getFullYear() + '-' + date.getMonth() + '-' + date.getDay() + '-' + date.getHours() + '-' + date.getMinutes() + '-' + date.getMilliseconds(); 205 | let oldConfContent = ""; 206 | switch (file) { 207 | case "config.sh": 208 | oldConfContent = getFileContentByName(confFile); 209 | fs.writeFileSync(bakConfFile, oldConfContent); 210 | break; 211 | case "crontab.list": 212 | oldConfContent = getFileContentByName(crontabFile); 213 | fs.writeFileSync(bakConfFile, oldConfContent); 214 | break; 215 | default: 216 | break; 217 | } 218 | 219 | } 220 | 221 | /** 222 | * 将 post 提交内容写入 config.sh 文件(同时备份旧的 config.sh 文件到 bak 目录) 223 | * @param content 224 | */ 225 | function saveNewConf(file, content) { 226 | bakConfFile(file); 227 | switch (file) { 228 | case "config.sh": 229 | fs.writeFileSync(confFile, content); 230 | break; 231 | case "crontab.list": 232 | fs.writeFileSync(crontabFile, content); 233 | execSync('crontab ' + crontabFile); 234 | break; 235 | default: 236 | break; 237 | } 238 | } 239 | 240 | /** 241 | * 获取文件内容 242 | * @param fileName 文件路径 243 | * @returns {string} 244 | */ 245 | function getFileContentByName(fileName) { 246 | if (fs.existsSync(fileName)) { 247 | return fs.readFileSync(fileName, 'utf8'); 248 | } 249 | return ''; 250 | } 251 | 252 | /** 253 | * 获取目录中最后修改的文件的路径 254 | * @param dir 目录路径 255 | * @returns {string} 最新文件路径 256 | */ 257 | function getLastModifyFilePath(dir) { 258 | var filePath = ''; 259 | 260 | if (fs.existsSync(dir)) { 261 | var lastmtime = 0; 262 | 263 | var arr = fs.readdirSync(dir); 264 | 265 | arr.forEach(function (item) { 266 | var fullpath = path.join(dir, item); 267 | var stats = fs.statSync(fullpath); 268 | if (stats.isFile()) { 269 | if (stats.mtimeMs >= lastmtime) { 270 | filePath = fullpath; 271 | } 272 | } 273 | }); 274 | } 275 | return filePath; 276 | } 277 | 278 | 279 | var app = express(); 280 | // gzip压缩 281 | app.use(compression({ level: 6, filter: shouldCompress })); 282 | 283 | function shouldCompress(req, res) { 284 | if (req.headers['x-no-compression']) { 285 | // don't compress responses with this request header 286 | return false; 287 | } 288 | 289 | // fallback to standard filter function 290 | return compression.filter(req, res); 291 | } 292 | 293 | app.use(session({ 294 | secret: 'secret', 295 | name: `connect.${Math.random()}`, 296 | resave: true, 297 | saveUninitialized: true 298 | })); 299 | app.use(bodyParser.json({ limit: '50mb' })); 300 | app.use(bodyParser.urlencoded({ limit: '50mb', extended: true })); 301 | app.use(express.static(path.join(__dirname, 'public'))); 302 | 303 | /** 304 | * 登录页面 305 | */ 306 | app.get('/', function (request, response) { 307 | if (request.session.loggedin) { 308 | response.redirect('./home'); 309 | } else { 310 | response.sendFile(path.join(__dirname + '/public/auth.html')); 311 | } 312 | }); 313 | 314 | /** 315 | * 用户名密码 316 | */ 317 | app.get('/changepwd', function (request, response) { 318 | if (request.session.loggedin) { 319 | response.sendFile(path.join(__dirname + '/public/pwd.html')); 320 | } else { 321 | response.redirect('/'); 322 | } 323 | }); 324 | 325 | /** 326 | * 获取二维码链接 327 | */ 328 | 329 | app.get('/qrcode', function (request, response) { 330 | if (request.session.loggedin) { 331 | (async () => { 332 | try { 333 | await step1(); 334 | const qrurl = await step2(); 335 | if (qrurl != 0) { 336 | response.send({ err: 0, qrcode: qrurl }); 337 | } else { 338 | response.send({ err: 1, msg: "错误" }); 339 | } 340 | } catch (err) { 341 | response.send({ err: 1, msg: err }); 342 | } 343 | })(); 344 | } else { 345 | response.send({ err: 1, msg: loginFaild }); 346 | } 347 | }) 348 | 349 | /** 350 | * 获取返回的cookie信息 351 | */ 352 | 353 | app.get('/cookie', function (request, response) { 354 | if (request.session.loggedin && cookies != "") { 355 | (async () => { 356 | try { 357 | const cookie = await checkLogin(); 358 | if (cookie.body.errcode == 0) { 359 | let ucookie = getCookie(cookie); 360 | response.send({ err: 0, cookie: ucookie }); 361 | } else { 362 | response.send({ err: cookie.body.errcode, msg: cookie.body.message }); 363 | } 364 | } catch (err) { 365 | response.send({ err: 1, msg: err }); 366 | } 367 | })(); 368 | } else { 369 | response.send({ err: 1, msg: loginFaild }); 370 | } 371 | }) 372 | 373 | /** 374 | * 获取各种配置文件api 375 | */ 376 | 377 | app.get('/api/config/:key', function (request, response) { 378 | if (request.session.loggedin) { 379 | if (configString.indexOf(request.params.key) > -1) { 380 | switch (request.params.key) { 381 | case 'config': 382 | content = getFileContentByName(confFile); 383 | break; 384 | case 'sample': 385 | content = getFileContentByName(sampleFile); 386 | break; 387 | case 'crontab': 388 | content = getFileContentByName(crontabFile); 389 | break; 390 | default: 391 | break; 392 | } 393 | response.setHeader("Content-Type", "text/plain"); 394 | response.send(content); 395 | } else { 396 | response.send("no config"); 397 | } 398 | } else { 399 | response.send(loginFaild); 400 | } 401 | }) 402 | 403 | /** 404 | * 首页 配置页面 405 | */ 406 | app.get('/home', function (request, response) { 407 | if (request.session.loggedin) { 408 | response.sendFile(path.join(__dirname + '/public/home.html')); 409 | } else { 410 | response.redirect('/'); 411 | } 412 | 413 | }); 414 | 415 | /** 416 | * 对比 配置页面 417 | */ 418 | app.get('/diff', function (request, response) { 419 | if (request.session.loggedin) { 420 | response.sendFile(path.join(__dirname + '/public/diff.html')); 421 | } else { 422 | response.redirect('/'); 423 | } 424 | 425 | }); 426 | 427 | /** 428 | * crontab 配置页面 429 | */ 430 | app.get('/crontab', function (request, response) { 431 | if (request.session.loggedin) { 432 | response.sendFile(path.join(__dirname + '/public/crontab.html')); 433 | } else { 434 | response.redirect('/'); 435 | } 436 | 437 | }); 438 | 439 | /** 440 | * 手动执行脚本 页面 441 | */ 442 | app.get('/run', function (request, response) { 443 | if (request.session.loggedin) { 444 | response.sendFile(path.join(__dirname + '/public/run.html')); 445 | } else { 446 | response.redirect('/'); 447 | } 448 | }); 449 | 450 | app.post('/runCmd', function(request, response) { 451 | if (request.session.loggedin) { 452 | const cmd = `cd ${rootPath};` + request.body.cmd; 453 | const delay = request.body.delay || 0; 454 | // console.log('before exec'); 455 | // exec maxBuffer 20MB 456 | exec(cmd, { maxBuffer: 1024 * 1024 * 20 }, (error, stdout, stderr) => { 457 | // console.log(error, stdout, stderr); 458 | // 根据传入延时返回数据,有时太快会出问题 459 | setTimeout(() => { 460 | if (error) { 461 | console.error(`执行的错误: ${error}`); 462 | response.send({ err: 1, msg: stdout ? `${stdout}${error}` : `${error}` }); 463 | return; 464 | 465 | } 466 | 467 | if (stdout) { 468 | // console.log(`stdout: ${stdout}`) 469 | response.send({ err: 0, msg: `${stdout}` }); 470 | return; 471 | 472 | } 473 | 474 | if (stderr) { 475 | console.error(`stderr: ${stderr}`); 476 | response.send({ err: 1, msg: `${stderr}` }); 477 | return; 478 | } 479 | 480 | response.send({ err: 0, msg: '执行结束,无结果返回。' }); 481 | }, delay); 482 | }); 483 | } else { 484 | response.redirect('/'); 485 | } 486 | }); 487 | 488 | /** 489 | * auth 490 | */ 491 | app.post('/auth', function (request, response) { 492 | let username = request.body.username; 493 | let password = request.body.password; 494 | fs.readFile(authConfigFile, 'utf8', function (err, data) { 495 | if (err) console.log(err); 496 | var con = JSON.parse(data); 497 | if (username && password) { 498 | if (username == con.user && password == con.password) { 499 | request.session.loggedin = true; 500 | request.session.username = username; 501 | response.send({ err: 0 }); 502 | } else { 503 | response.send({ err: 1, msg: authError }); 504 | } 505 | } else { 506 | response.send({ err: 1, msg: "请输入用户名密码!" }); 507 | } 508 | }); 509 | 510 | }); 511 | 512 | /** 513 | * change pwd 514 | */ 515 | app.post('/changepass', function (request, response) { 516 | if (request.session.loggedin) { 517 | let username = request.body.username; 518 | let password = request.body.password; 519 | let config = { 520 | user: username, 521 | password: password 522 | } 523 | if (username && password) { 524 | fs.writeFile(authConfigFile, JSON.stringify(config), function (err) { 525 | if (err) { 526 | response.send({ err: 1, msg: "写入错误请重试!" }); 527 | } else { 528 | response.send({ err: 0, msg: "更新成功!" }); 529 | } 530 | }); 531 | } else { 532 | response.send({ err: 1, msg: "请输入用户名密码!" }); 533 | } 534 | 535 | } else { 536 | response.send(loginFaild); 537 | 538 | } 539 | }); 540 | 541 | /** 542 | * change pwd 543 | */ 544 | app.get('/logout', function (request, response) { 545 | request.session.destroy() 546 | response.redirect('/'); 547 | 548 | }); 549 | 550 | /** 551 | * save config 552 | */ 553 | 554 | app.post('/api/save', function (request, response) { 555 | if (request.session.loggedin) { 556 | let postContent = request.body.content; 557 | let postfile = request.body.name; 558 | saveNewConf(postfile, postContent); 559 | response.send({ err: 0, title: "保存成功! ", msg: "将自动刷新页面查看修改后的 " + postfile + " 文件" }); 560 | } else { 561 | response.send({ err: 1, title: "保存失败! ", msg: loginFaild }); 562 | } 563 | 564 | }); 565 | 566 | /** 567 | * 日志查询 页面 568 | */ 569 | app.get('/log', function (request, response) { 570 | if (request.session.loggedin) { 571 | response.sendFile(path.join(__dirname + '/public/tasklog.html')); 572 | } else { 573 | response.redirect('/'); 574 | } 575 | }); 576 | 577 | /** 578 | * 日志列表 579 | */ 580 | app.get('/api/logs', function (request, response) { 581 | if (request.session.loggedin) { 582 | var fileList = fs.readdirSync(logPath, 'utf-8'); 583 | var dirs = []; 584 | var rootFiles = []; 585 | for (var i = 0; i < fileList.length; i++) { 586 | var stat = fs.lstatSync(logPath + fileList[i]); 587 | // 是目录,需要继续 588 | if (stat.isDirectory()) { 589 | var fileListTmp = fs.readdirSync(logPath + '/' + fileList[i], 'utf-8'); 590 | fileListTmp.reverse(); 591 | var dirMap = { 592 | dirName: fileList[i], 593 | files: fileListTmp 594 | } 595 | dirs.push(dirMap); 596 | } else { 597 | rootFiles.push(fileList[i]); 598 | } 599 | } 600 | 601 | dirs.push({ 602 | dirName: '@', 603 | files: rootFiles 604 | }); 605 | var result = { dirs }; 606 | response.send(result); 607 | 608 | } else { 609 | response.redirect('/'); 610 | } 611 | 612 | }); 613 | 614 | /** 615 | * 日志文件 616 | */ 617 | app.get('/api/logs/:dir/:file', function (request, response) { 618 | if (request.session.loggedin) { 619 | let filePath; 620 | if (request.params.dir === '@') { 621 | filePath = logPath + request.params.file; 622 | } else { 623 | filePath = logPath + request.params.dir + '/' + request.params.file; 624 | } 625 | var content = getFileContentByName(filePath); 626 | response.setHeader("Content-Type", "text/plain"); 627 | response.send(content); 628 | } else { 629 | response.redirect('/'); 630 | } 631 | 632 | }); 633 | 634 | checkConfigFile() 635 | 636 | app.listen(5678); -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 一、申明 2 | 3 | - 本脚本的原作者为 EvineDeng。 4 | 5 | - 本脚本只是给 lxk0301/jd_scripts 套层壳,用来运行其中的 js 脚本。 6 | 7 | - 请勿将本项目用于任何商业用途! 8 | 9 | # 二、如有帮助你薅到羊毛,请不吝赏杯茶水 10 | 11 | ![thanks](thanks.png) 12 | 13 | # 三、快速开始 14 | 15 | ## 1. Linux 16 | 17 | ### 脚本一键部署: 18 | 19 | 运行此脚本前必须手动安装好依赖:`git wget curl perl moreutils node.js npm` 20 | ```shell 21 | wget -q https://cdn.jsdelivr.net/gh/RikudouPatrickstar/jd-base/onekey-install.sh -O onekey-jd-base.sh && chmod +x onekey-jd-base.sh && ./onekey-jd-base.sh 22 | ``` 23 | 24 | ## 2. Docker 25 | 26 | ### 脚本一键部署单个容器: 27 | 28 | ```shell 29 | wget -q https://cdn.jsdelivr.net/gh/RikudouPatrickstar/jd-base/docker/onekey-docker.sh -O onekey-jd-docker.sh && chmod +x onekey-jd-docker.sh && ./onekey-jd-docker.sh 30 | ``` 31 | 32 | 安装 [containrrr/watchtower](https://containrrr.dev/watchtower/) 可自动更新 Docker 容器,更详细的部署说明,请自行查阅其文档。[Docker 相关文件](docker/) 已全部提供,其他玩法自行研究。 33 | 34 | # 四、脚本相关说明 35 | 36 | `Web-控制面板` 已集成手动执行脚本的功能,下列内容为命令行操作的示例。 37 | 38 | 1. 手动执行 [git_pull.sh](git_pull.sh),作用如下: 39 | 40 | - 自动更新 jd-base 套壳工具脚本和 jd_scripts 的京东薅羊毛脚本 41 | 42 | - 自动删除失效的定时任务、添加新的定时任务,并发送通知 43 | 44 | - 检测配置文件模板 `config.sh.sample` 是否升版,如有升版,发出通知 45 | 46 | ```shell 47 | # Linux 48 | cd {项目安装目录} 49 | bash git_pull.sh 50 | 51 | # Docker 52 | docker exec -it <容器名> bash git_pull.sh 53 | ``` 54 | 55 | 2. 手动执行 [rm_log.sh](rm_log.sh) 删除指定时间以前的旧日志 56 | 57 | ```shell 58 | # Linux 59 | cd {项目安装目录} 60 | bash rm_log.sh 61 | 62 | # Docker 63 | docker exec -it <容器名> bash rm_log.sh 64 | ``` 65 | 66 | 3. 手动执行 [export_sharecodes.sh](export_sharecodes.sh) 从日志中按模板导出所有互助码 67 | 68 | ```shell 69 | # Linux 70 | cd {项目安装目录} 71 | bash export_sharecodes.sh 72 | 73 | # Docker 74 | docker exec -it <容器名> bash export_sharecodes.sh 75 | ``` 76 | 77 | 4. 手动执行薅羊毛脚本,用法如下(其中 `xxx` 为 jd_scripts 中的脚本名称,请勿直接以 `node xxx.js` 命令运行,jd.sh 的作用是为 js 脚本导入必须的环境变量并运行): 78 | 79 | ```shell 80 | # Linux 81 | cd {项目安装目录} 82 | bash jd.sh xxx # 如果设置了随机延迟并且当时时间不在0-2、30-31、59分内,将随机延迟一定秒数 83 | bash jd.sh xxx now # 无论是否设置了随机延迟,均立即运行 84 | 85 | # Docker 86 | docker exec -it <容器名> bash jd.sh xxx # 如果设置了随机延迟并且当时时间不在0-2、30-31、59分内,将随机延迟一定秒数 87 | docker exec -it <容器名> bash jd.sh xxx now # 无论是否设置了随机延迟,均立即运行 88 | ``` 89 | 90 | 5. 手动启动挂机程序 91 | 92 | ```shell 93 | # Linux 94 | cd {项目安装目录} 95 | bash jd.sh hangup 96 | 97 | # Docker 98 | docker exec -it <容器名> bash jd.sh hangup 99 | ``` 100 | 然后挂机脚本就会一直运行,目前仅一个 `jd_crazy_joy_coin.js` 为挂机脚本。 101 | 102 | 6. jd-base 脚本已完全开源,更多细节请自行查阅源码。 103 | 104 | # 五、Web 面板使用说明 105 | 106 | 下面内容是针对非 Docker 用户的,Docker 中这些流程都做好了,直接使用即可。 107 | 108 | ## 使用流程 109 | 110 | 1. 面板目录为 {项目安装目录}/panel 111 | 112 | 2. 手动启动,根据需要二选一。 113 | 114 | ```shell 115 | # 1. 如需要编辑保存好就结束掉在线页面(保存好后按 Ctrl + C 结束) 116 | node server.js 117 | 118 | # 2. 如需一直后台运行,以方便随时在线编辑 119 | npm install -g pm2 # npm和yarn二选一 120 | yarn global add pm2 # npm和yarn二选一 121 | pm2 start server.js 122 | 123 | # 2.1 如果需要开机自启 124 | pm2 save && pm2 startup 125 | ``` 126 | 127 | 4. 访问 `http://:5678` 登陆、编辑并保存即可(初始用户名:`admin`,初始密码:`password`)。如无法访问,请从防火墙、端口转发、网络方面着手解决。 128 | 129 | 5. 如需要重置面板密码,cd 到本仓库的目录下输入 `bash jd.sh resetpwd`。 130 | -------------------------------------------------------------------------------- /rm_log.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## 判断环境 4 | ShellDir=$(cd "$(dirname "$0")";pwd) 5 | LogDir=${ShellDir}/log 6 | 7 | ## 导入配置文件 8 | . ${ShellDir}/config/config.sh 9 | 10 | 11 | ## 删除运行 js 脚本的旧日志 12 | function Rm_JsLog { 13 | LogFileList=$(ls -l ${LogDir}/*/*.log | awk '{print $9}') 14 | for log in ${LogFileList} 15 | do 16 | # 文件名比文件属性获得的日期要可靠 17 | LogDate=$(echo ${log} | awk -F "/" '{print $NF}' | cut -c1-10) 18 | if [[ $(uname -s) == Darwin ]] 19 | then 20 | DiffTime=$(($(date +%s) - $(date -j -f "%Y-%m-%d" "${LogDate}" +%s))) 21 | else 22 | DiffTime=$(($(date +%s) - $(date +%s -d "${LogDate}"))) 23 | fi 24 | [ ${DiffTime} -gt $((${RmLogDaysAgo} * 86400)) ] && rm -vf ${log} 25 | done 26 | } 27 | 28 | 29 | ## 删除 git_pull.sh 的运行日志 30 | function Rm_GitPullLog { 31 | if [[ $(uname -s) == Darwin ]] 32 | then 33 | DateDelLog=$(date -v-${RmLogDaysAgo}d "+%Y-%m-%d") 34 | else 35 | Stmp=$(($(date "+%s") - 86400 * ${RmLogDaysAgo})) 36 | DateDelLog=$(date -d "@${Stmp}" "+%Y-%m-%d") 37 | fi 38 | LineEndGitPull=$[$(cat ${LogDir}/git_pull.log | grep -n "${DateDelLog} " | head -1 | awk -F ":" '{print $1}') - 3] 39 | [ ${LineEndGitPull} -gt 0 ] && perl -i -ne "{print unless 1 .. ${LineEndGitPull} }" ${LogDir}/git_pull.log 40 | } 41 | 42 | 43 | ## 删除空文件夹 44 | function Rm_EmptyDir { 45 | cd ${LogDir} 46 | for dir in $(ls) 47 | do 48 | if [ -d ${dir} ] && [[ $(ls ${dir}) == "" ]]; then 49 | rm -rf ${dir} 50 | fi 51 | done 52 | } 53 | 54 | 55 | ## 运行 56 | if [ -n "${RmLogDaysAgo}" ]; then 57 | echo -e "查找旧日志文件中...\n" 58 | Rm_JsLog 59 | Rm_GitPullLog 60 | Rm_EmptyDir 61 | echo -e "删除旧日志执行完毕\n" 62 | fi 63 | -------------------------------------------------------------------------------- /sample/auth.json: -------------------------------------------------------------------------------- 1 | {"user":"admin","password":"password"} -------------------------------------------------------------------------------- /sample/config.sh.sample: -------------------------------------------------------------------------------- 1 | ## Version: v3.44.11 2 | ## Date: 2021-03-24 3 | ## Update Content: 移除活动 “城城领现金” 4 | 5 | ## 上面版本号中,如果第 2 位数字有变化,那么代表推荐更新,如果只有第 3 位数字有变化,则可更新可不更新 6 | 7 | ##-------------------------------- 说明 --------------------------------## 8 | ## 以下配置中,带有 export 申明的,均由 lxk0301 定义,详见:https://github.com/RikudouPatrickstar/jd_scripts/blob/master/githubAction.md#%E4%B8%8B%E6%96%B9%E6%8F%90%E4%BE%9B%E4%BD%BF%E7%94%A8%E5%88%B0%E7%9A%84-secrets%E5%85%A8%E9%9B%86%E5%90%88 9 | ## 其他互助码,考虑到直接按 lxk0301 定义的变量去填的话,一是不方便记忆,二是容易搞混,所以最终的变量将由脚本去组合,只要按注释去填即可 10 | ## 除此之外,还额外增加了是否自动删除失效任务 AutoDelCron、是否自动增加新任务 AutoAddCron、删除旧日志时间 RmLogDaysAgo、随机延迟启动任务 RandomDelay、是否添加自定义脚本 EnableExtraShell 五个人性化的设置供选择 11 | ## config.sh 的本质是一个 shell 脚本,更多玩法自行 DIY 12 | 13 | 14 | ##-------------------------------- 定义 Cookie(必填) --------------------------------## 15 | ## 请依次填入每个用户的 Cookie,Cookie 的具体形式(只有 pt_key 字段和 pt_pin 字段,没有其他字段):pt_key=xxxxxxxxxx;pt_pin=xxxx; 16 | ## 1. 如果是通过控制面板编辑本文件,点击页面上方 “扫码获取 Cookie” 即可获取 17 | ## 2. 还可以通过浏览器开发工具获取,教程:https://github.com/RikudouPatrickstar/jd_scripts/wiki/GetCookies 18 | ## 必须按数字顺序 1、2、3、4... 依次编号下去,例子只有 3 个,超出 3 个继续往下编号即可 19 | ## 不允许有汉字,如果 ID 有汉字,请在 PC 浏览器上获取 Cookie,会自动将汉字转换为 URL 编码 20 | Cookie1="" 21 | Cookie2="" 22 | Cookie3="" 23 | 24 | 25 | ##-------------------------------- 临时屏蔽某个 Cookie(选填) --------------------------------## 26 | ## 如果某些 Cookie 已经失效了,但暂时还没法更新,可以使用此功能在不删除该 Cookie 和重新修改 Cookie 编号的前提下,临时屏蔽掉某些编号的 Cookie 27 | ## 多个 Cookie 编号以半角的空格分隔,两侧一对半角双引号,使用此功能后,在运行 js 脚本时账户编号将发生变化 28 | ## 举例 1:TempBlockCookie="2" 临时屏蔽掉 Cookie2 29 | ## 举例 2:TempBlockCookie="2 4" 临时屏蔽掉 Cookie2 和 Cookie4 30 | ## 如果只是想要屏蔽某个账号不玩某些小游戏,可以参考下面 case 这个命令的例子来控制,脚本名称请去掉后缀“.js” 31 | ## case $1 in 32 | ## jd_fruit) 33 | ## TempBlockCookie="5" # 账号 5 不玩东东农场 34 | ## ;; 35 | ## jd_dreamFactory | jd_jdfactory) 36 | ## TempBlockCookie="2" # 账号 2 不玩京喜工厂和东东工厂 37 | ## ;; 38 | ## esac 39 | TempBlockCookie="" 40 | 41 | 42 | ##-------------------------------- 定义是否自动删除失效的脚本与定时任务(选填) --------------------------------## 43 | ## 有的时候,某些 js 脚本只在特定的时间有效,过了时间就失效了,需要自动删除失效的本地定时任务,则设置为 "true" ,否则请设置为 "false" 44 | ## 检测文件:RikudouPatrickstar/jd_scripts 仓库中的 docker/crontab_list.sh 45 | ## 当设置为 "true" 时,会自动从检测文件中读取比对删除的任务(识别以 “jd_”、“jr_”、“jx_” 开头的任务) 46 | ## 当设置为 "true" 时,脚本只会删除一整行失效的定时任务,不会修改其他现有任务,所以任何时候,你都可以自己调整你的 crontab.list 47 | ## 当设置为 "true" 时,如果你有添加额外脚本是以 “jd_”、“jr_”、“jx_” 开头的,如检测文件中,会被删除,不是以 “jd_”、“jr_”、“jx_” 开头的任务则不受影响 48 | AutoDelCron="true" 49 | 50 | 51 | ##-------------------------------- 定义是否自动增加新的本地定时任务(选填) --------------------------------## 52 | ## lxk0301 会在有需要的时候,增加定时任务,如需要本地自动增加新的定时任务,则设置为 "true" ,否则请设置为 "false" 53 | ## 检测文件:RikudouPatrickstar/jd_scripts 仓库中的 docker/crontab_list.sh 54 | ## 当设置为 "true" 时,如果检测到检测文件中有增加新的定时任务,那么在本地也增加(识别以 “jd_”、“jr_”、“jx_” 开头的任务) 55 | ## 当设置为 "true" 时,会自动从检测文件新增加的任务中读取时间,该时间为北京时间 56 | ## 当设置为 "true" 时,脚本只会增加新的定时任务,不会修改其他现有任务,所以任何时候,你都可以自己调整你的 crontab.list 57 | AutoAddCron="true" 58 | 59 | 60 | ##-------------------------------- 定义删除日志的时间(选填) --------------------------------## 61 | ## 定义在运行删除旧的日志任务时,要删除多少天以前的日志,请输入正整数,不填则禁用删除日志的功能 62 | RmLogDaysAgo="7" 63 | 64 | 65 | ##-------------------------------- 定义随机延迟启动任务(选填) --------------------------------## 66 | ## 如果任务不是必须准点运行的任务,那么给它增加一个随机延迟,由你定义最大延迟时间,单位为秒,如 RandomDelay="300" ,表示任务将在 1-300 秒内随机延迟一个秒数,然后再运行 67 | ## 在 crontab.list 中,在每小时第 0-2 分、第 30-31 分、第 59 分这几个时间内启动的任务,均算作必须准点运行的任务,在启动这些任务时,即使你定义了 RandomDelay,也将准点运行,不启用随机延迟 68 | ## 在 crontab.list 中,除掉每小时上述时间启动的任务外,其他任务在你定义了 RandomDelay 的情况下,一律启用随机延迟,但如果你按照教程给某些任务添加了 "now",那么这些任务也将无视随机延迟直接启动 69 | RandomDelay="300" 70 | 71 | 72 | ##-------------------------------- 定义 User-Agent(选填) --------------------------------## 73 | ## 自定义 jd_scripts 仓库里京东系列 js 脚本的 User-Agent,不了解 User-Agent 的请不要随意填写内容 74 | ## 如需使用,请自行解除下一行注释 75 | # export JD_USER_AGENT="" 76 | 77 | 78 | ##-------------------------------- 定义 jd_scripts 以外的 js 脚本(选填) --------------------------------## 79 | ## 自定义 jd_scripts 仓库以外的 js 脚本存放目录,jd.sh 会尝试调用,设置完后请手动执行 git_pull.sh 80 | ## 推荐存放在 config 目录下,可以填多个目录,以 shell 数组的形式填写,使用相对路径,例如 ("config/myscripts1" "config/myscripts2") 81 | ## 对应的定时任务自行设置,暂不支持脚本名称以 "jd_"、"jr_"、"jx_" 开头,后续可能会改进 82 | DiyDirs=("") 83 | ## 选用的目录下自行放置 package.json,如需使用 lxk0301/jd_scripts 的 package.json,则下面 DiyPackgeJson 设置为 "false" 84 | DiyPackgeJson="false" 85 | 86 | 87 | ##-------------------------------- 定义替换 jd_scripts 中的 js 脚本(选填) --------------------------------## 88 | ## 自定义替换 jd_scripts 中出现问题尚未修复的脚本,执行 git_pull.sh 时将自动替换 89 | ## 例如,假设 jd_dreamFactory 脚本出了问题,作者尚未及时修复,而已经有人修复,或者你自己修复了,其 url 为 "https://abc.xyz/xxxx/jd_dreamFactory.js",则可以在这里设置 ReplaceJsName="jd_dreamFactory",ReplaceJsUrl="https://abc.xyz/xxxx/jd_dreamFactory.js" 进行替换 90 | ## 如果有多个脚本需要替换,请将下面两个变量以 shell 数组的形式填写,其中脚本名称和脚本超链接必须一一对应 91 | ## 多脚本替换填写示例: 92 | ## ReplaceJsName=("jd_fruit" "jd_dreamFactory" "jd_jxnc") 93 | ## ReplaceJsUrl=("https://def.com/xxxx/jd_fruit.js" 94 | ## "https://abc.xyz/xxxx/jd_dreamFactory.js" 95 | ## "https://ghi.rip/xxxx/jd_jxnc.js") 96 | ReplaceJsName=("") 97 | ReplaceJsUrl=("") 98 | 99 | 100 | ##-------------------------------- 定义通知 TOKEN(选填) --------------------------------## 101 | ## 想通过什么渠道收取通知,就填入对应渠道的值 102 | ## 1. ServerChan 或 ServerChan Turbo,教程:http://sc.ftqq.com/ 或 https://sct.ftqq.com/ 103 | export PUSH_KEY="" 104 | 105 | ## 2. BARK,教程:https://github.com/RikudouPatrickstar/jd_scripts/blob/master/githubAction.md#%E4%B8%8B%E6%96%B9%E6%8F%90%E4%BE%9B%E4%BD%BF%E7%94%A8%E5%88%B0%E7%9A%84-secrets%E5%85%A8%E9%9B%86%E5%90%88 106 | export BARK_PUSH="" 107 | export BARK_SOUND="" 108 | 109 | ## 3. 钉钉,教程:https://github.com/RikudouPatrickstar/jd_scripts/blob/master/githubAction.md#%E4%B8%8B%E6%96%B9%E6%8F%90%E4%BE%9B%E4%BD%BF%E7%94%A8%E5%88%B0%E7%9A%84-secrets%E5%85%A8%E9%9B%86%E5%90%88 110 | export DD_BOT_TOKEN="" 111 | export DD_BOT_SECRET="" 112 | 113 | ## 4. iGot 聚合推送,支持多方式推送,填写 iGot 的推送 key,教程:https://wahao.github.io/Bark-MP-helper/#/ 114 | export IGOT_PUSH_KEY="" 115 | 116 | ## 5. Push Plus,微信扫码登录后一对一推送或一对多推送,参考文档:http://www.pushplus.plus/ 117 | ## 其中 PUSH_PLUS_USER 是一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码 118 | ## 注:(1、需订阅者扫描二维码 2、如果您是创建群组所属人,也需点击 “查看二维码” 扫描绑定,否则不能接受群组消息推送 3、只填 PUSH_PLUS_TOKEN 默认为一对一推送) 119 | export PUSH_PLUS_TOKEN="" 120 | export PUSH_PLUS_USER="" 121 | 122 | ## 6. 企业微信机器人消息推送 webhook 后面的 key,文档:https://work.weixin.qq.com/api/doc/90000/90136/91770 123 | export QYWX_KEY="" 124 | 125 | ## 7. 企业微信应用消息推送的值,文档:https://work.weixin.qq.com/api/doc/90000/90135/90236 126 | ## 依次填上 corpid 的值,corpsecret 的值,touser 的值,agentid,media_id 的值,注意用,号隔开,例如:"wwcff56746d9adwers,B-791548lnzXBE6_BWfxdf3kSTMJr9vFEPKAbh6WERQ,mingcheng,1000001,2COXgjH2UIfERF2zxrtUOKgQ9XklUqMdGSWLBoW_lSDAdafat" 127 | export QYWX_AM="" 128 | 129 | ## 8. Telegram,如需使用,TG_BOT_TOKEN 和 TG_USER_ID 必须同时赋值,教程:https://github.com/RikudouPatrickstar/jd_scripts/blob/master/backUp/TG_PUSH.md 130 | export TG_BOT_TOKEN="" 131 | export TG_USER_ID="" 132 | 133 | 134 | ##-------------------------------- Telegram 代理(选填) --------------------------------## 135 | ## Telegram 代理的 IP,代理类型为 http,比如你代理是 http://127.0.0.1:1080,则填写 export TG_PROXY_HOST="127.0.0.1",export TG_PROXY_PORT="1080" 136 | ## 如需使用,请自行解除注释 137 | # export TG_PROXY_HOST="" 138 | # export TG_PROXY_PORT="" 139 | 140 | ## Telegram API自建的反向代理地址,例子:反向代理地址 http://aaa.bbb.ccc 则填写 aaa.bbb.ccc,教程:https://www.hostloc.com/thread-805441-1-1.html 141 | ## 如需使用,请自行解除下注释 142 | # export TG_API_HOST="" 143 | 144 | 145 | ##-------------------------------- 定义每日签到的通知形式(选填) --------------------------------## 146 | ## js 脚本每日签到提供3种通知方式,分别为: 147 | ## 关闭通知,那么请在下方填入 0 148 | ## 简洁通知,那么请在下方填入 1,如果不填也默认为 1,其效果见:https://github.com/RikudouPatrickstar/jd_scripts/blob/master/icon/bean_sign_simple.jpg 149 | ## 原始通知,那么请在下方填入 2,内容比较长 150 | NotifyBeanSign="" 151 | 152 | 153 | ##-------------------------------- 定义每日签到每个接口间的延迟时间(选填) --------------------------------## 154 | ## 默认每个签到接口并发无延迟,如需要依次进行每个接口,请自定义延迟时间,单位为毫秒,延迟作用于每个签到接口, 如填入延迟则切换顺序签到(耗时较长) 155 | export JD_BEAN_STOP="" 156 | 157 | 158 | ##-------------------------------- 定义 jd_fruit 是否静默运行(选填) --------------------------------## 159 | ## 默认为 "false",不静默,发送推送通知消息,如不想收到通知,请修改为 "true" 160 | ## 如果你不想完全关闭或者完全开启通知,只想在特定的时间发送通知,可以参考下面的 “定义东东萌宠是否静默运行” 部分,设定几个 if 判断条件 161 | export FRUIT_NOTIFY_CONTROL="" 162 | 163 | ##-------------------------------- 定义 jd_fruit 是否使用水滴换豆卡(选填) --------------------------------## 164 | ## 如果出现限时活动时 100g 水换 20 豆,此时比浇水划算,"true" 表示换豆(不浇水),"false" 表示不换豆(继续浇水),默认是 "false" 165 | ## 如需切换为换豆(不浇水),请修改为 "true" 166 | export FRUIT_BEAN_CARD="" 167 | 168 | 169 | ##-------------------------------- 定义 jd_superMarket 蓝币兑换数量(选填) --------------------------------## 170 | ## 东东超市蓝币兑换,可用值包括: 171 | ## 一、0:表示不兑换京豆,这也是 jd_scripts 脚本的默认值 172 | ## 二、20:表示兑换 20 个京豆 173 | ## 三、1000:表示兑换 1000 个京豆 174 | ## 四、可兑换清单的商品名称,输入能跟唯一识别出来的关键词即可,比如:MARKET_COIN_TO_BEANS="抽纸" 175 | ## 注意:有些比较贵的实物商品京东只是展示出来忽悠人的,即使你零点用脚本去抢,也会提示没有或提示已下架 176 | export MARKET_COIN_TO_BEANS="0" 177 | 178 | ##-------------------------------- 定义 jd_superMarket 蓝币成功兑换奖品是否静默运行(选填) --------------------------------## 179 | ## 默认 "false" 关闭(即:奖品兑换成功后会发出通知提示),如需要静默运行不发出通知,请改为 "true" 180 | export MARKET_REWARD_NOTIFY="" 181 | 182 | ##-------------------------------- 定义 jd_superMarket 是否自动升级商品和货架(选填) --------------------------------## 183 | ## 升级顺序:解锁升级商品、升级货架,默认 "true" 自动升级,如需关闭自动升级,请改为 "false" 184 | export SUPERMARKET_UPGRADE="" 185 | 186 | ##-------------------------------- 定义 jd_superMarket 是否自动使用金币去抽奖(选填) --------------------------------## 187 | ## 是否用金币去抽奖,默认 "false" 关闭,如需开启,请修改为 "true" 188 | export SUPERMARKET_LOTTERY="" 189 | 190 | ##-------------------------------- 定义 jd_superMarket 是否自动参加PK队伍(选填) --------------------------------## 191 | ## 是否每次 PK 活动参加脚本作者创建的 PK 队伍,"true" 表示参加,"false" 表示不参加,默认为 "true" 192 | export JOIN_PK_TEAM="" 193 | 194 | 195 | ##-------------------------------- 定义 jd_joy 喂食克数(选填) --------------------------------## 196 | ## 你期望的宠汪汪每次喂食克数,只能填入 10、20、40、80,默认为 10 197 | ## 如实际持有食物量小于所设置的克数,脚本会自动降一档,直到降无可降 198 | ## 具体情况请自行在宠汪汪游戏中去查阅攻略 199 | export JOY_FEED_COUNT="" 200 | 201 | ##-------------------------------- 定义 jd_joy 兑换京豆数量(选填) --------------------------------## 202 | ## 目前的可用值包括:0、20、500、1000,其中 0 表示为不自动兑换京豆,如不设置,将默认为 "0" 203 | ## 不同等级可兑换不同数量的京豆,详情请见宠汪汪游戏中兑换京豆选项 204 | ## 500、1000 的京豆每天有总量限制,设置了并且你也有足够积分时,也并不代表就一定能抢到 205 | export JD_JOY_REWARD_NAME="" 206 | 207 | ##-------------------------------- 定义 jd_joy 兑换京豆是否静默运行(选填) --------------------------------## 208 | ## 默认为 "false",在成功兑换京豆时将发送推送通知消息(失败不发送),如想要静默不发送通知,请修改为 "true" 209 | export JD_JOY_REWARD_NOTIFY="" 210 | 211 | ##-------------------------------- 定义 jd_joy 是否自动给好友的汪汪喂食(选填) --------------------------------## 212 | ## 默认 "true" 不会自动给好友的汪汪喂食,如想自动喂食,请改成 "false" 213 | export JOY_HELP_FEED="" 214 | 215 | ##-------------------------------- 定义 jd_joy 是否自动报名宠物赛跑(选填) --------------------------------## 216 | ## 默认 "true" 参加宠物赛跑,如需关闭,请改成 "false" 217 | export JOY_RUN_FLAG="" 218 | 219 | ##-------------------------------- 定义 jd_joy 参加比赛类型(选填) --------------------------------## 220 | ## 当 JOY_RUN_FLAG 不设置或设置为 "true" 时生效 221 | ## 可选值:2,10,50,其他值不可以。其中 2 代表参加双人 PK 赛,10 代表参加 10 人突围赛,50 代表参加 50 人挑战赛,不填时默认为 2 222 | ## 各个账号间请使用 & 分隔,比如:JOY_TEAM_LEVEL="2&2&50&10" 223 | ## 如果你有 5 个账号但只写了四个数字,那么第 5 个账号将默认参加 2 人赛,账号如果更多,与此类似 224 | export JOY_TEAM_LEVEL="" 225 | 226 | ##-------------------------------- 定义 jd_joy 赛跑互助(选填) --------------------------------## 227 | ## 控制 jd_joy_run.js 脚本宠汪汪赛跑是否自己账号内部互助,默认为 "false" 给脚本作者 lxk0301 内置的账号助力,输入 "true" 开启内部互助 228 | export JOY_RUN_HELP_MYSELF="" 229 | 230 | ##-------------------------------- 定义 jd_joy 赛跑获胜后是否推送通知(选填) --------------------------------## 231 | ## 控制 jd_joy.js 脚本宠汪汪赛跑获胜后是否推送通知,"false" 为否(不推送通知消息),"true" 为是(即:发送推送通知消息),默认为 "true" 232 | export JOY_RUN_NOTIFY="" 233 | 234 | 235 | ##-------------------------------- 定义 jd_moneyTree 是否自动将金果卖出变成金币(选填) --------------------------------## 236 | ## 摇钱树金币有时效,默认为 "false",不卖出金果为金币,如想希望自动卖出,请修改为 "true" 237 | export MONEY_TREE_SELL_FRUIT="" 238 | 239 | 240 | ##-------------------------------- 定义 jd_pet 是否静默运行(选填) --------------------------------## 241 | ## 默认 "false"(不静默,发送推送通知消息),如想静默请修改为 true 242 | ## 每次执行脚本通知太频繁了,改成只在周三和周六中午那一次运行时发送通知提醒 243 | ## 除掉上述提及时间之外,均设置为 true,静默不发通知 244 | ## 特别说明:针对北京时间有效。 245 | if [ $(date "+%w") -eq 6 ] && [ $(date "+%H") -ge 9 ] && [ $(date "+%H") -lt 14 ]; then 246 | export PET_NOTIFY_CONTROL="false" 247 | elif [ $(date "+%w") -eq 3 ] && [ $(date "+%H") -ge 9 ] && [ $(date "+%H") -lt 14 ]; then 248 | export PET_NOTIFY_CONTROL="false" 249 | else 250 | export PET_NOTIFY_CONTROL="true" 251 | fi 252 | 253 | 254 | ##-------------------------------- 定义 jd_dreamFactory 控制哪个京东账号不运行此脚本(选填) --------------------------------## 255 | ## 输入 "1" 代表第一个京东账号不运行,多个使用 & 连接,例:"1&3" 代表账号 1 和账号 3 不运行京喜工厂脚本,注:输入 "0",代表全部账号不运行京喜工厂脚本 256 | ## 如果使用了 “临时屏蔽某个Cookie” TempBlockCookie 功能,编号会发生变化 257 | export DREAMFACTORY_FORBID_ACCOUNT="" 258 | 259 | 260 | ##-------------------------------- 定义 jd_jdfactory 控制哪个京东账号不运行此脚本(选填) --------------------------------## 261 | ## 输入 "1" 代表第一个京东账号不运行,多个使用 & 连接,例:"1&3" 代表账号 1 和账号 3 不运行东东工厂脚本,注:输入 "0",代表全部账号不运行东东工厂脚本 262 | ## 如果使用了 “临时屏蔽某个Cookie” TempBlockCookie 功能,编号会发生变化 263 | export JDFACTORY_FORBID_ACCOUNT="" 264 | 265 | ##-------------------------------- 定义 jd_jdfactory 心仪的商品(选填) --------------------------------## 266 | ## 只有在满足以下条件时,才自动投入电力:一是存储的电力满足生产商品所需的电力,二是心仪的商品有库存,如果没有输入心仪的商品,那么当前你正在生产的商品视作心仪的商品 267 | ## 如果你看不懂上面的话,请去东东工厂游戏中查阅攻略 268 | ## 心仪的商品请输入商品的全称或能唯一识别出该商品的关键字 269 | export FACTORAY_WANTPRODUCT_NAME="" 270 | 271 | 272 | ##-------------------------------- 定义 jd_jxnc 通知级别(选填) --------------------------------## 273 | ## 可用值: 0(不通知); 1(本次获得水滴 > 0); 2(任务执行); 3(任务执行 + 未种植种子),默认为 "3" 274 | export JXNC_NOTIFY_LEVEL="3" 275 | 276 | 277 | ##-------------------------------- 定义 jd_crazy_joy 相关设置(选填) --------------------------------## 278 | ## 疯狂的 JOY 循环助力,"true" 表示循环助力,"false" 表示不循环助力,默认 "false" 279 | export JDJOY_HELPSELF="" 280 | 281 | ## 疯狂的 JOY 京豆兑换,目前最小值为 500/1000 京豆,默认为 "0" 不开启京豆兑换 282 | export JDJOY_APPLYJDBEAN="" 283 | 284 | ## 疯狂的 JOY 自动购买什么等级的 JOY,如需要使用请自行解除注释 285 | # export BUY_JOY_LEVEL="" 286 | 287 | 288 | ##-------------------------------- 定义 jd_cfd 通知开关(选填) --------------------------------## 289 | ## 定义京喜财富岛运行脚本后是否通知,默认 "false" 不通知,如需要通知,请设置为 "true" 290 | export CFD_NOTIFY_CONTROL="" 291 | 292 | 293 | ##-------------------------------- 定义 jd_jxd 是否自动兑换兑币(选填) --------------------------------## 294 | ## 定义京小兑是否自动把抽奖卷兑换为兑币,默认否,如需自动兑换,请设置为 "true",不填则为不兑换 295 | export JD_JXD_EXCHANGE="" 296 | 297 | 298 | ##-------------------------------- 定义 jd_necklace 是否静默运行(选填) -------------------------------- ## 299 | ## 控制点点券是否静默运行,"false" 为否(默认值 "false",发送推送通知消息),"true" 为是(即:不发送推送通知消息) 300 | export DDQ_NOTIFY_CONTROL="" 301 | 302 | 303 | ##-------------------------------- 定义 jd_cash 是否静默运行(选填) -------------------------------- ## 304 | ## 控制签到领现金是否静默运行,"false" 为否(默认值 "false",发送推送通知消息),"true" 为是(即:不发送推送通知消息) 305 | export CASH_NOTIFY_CONTROL="" 306 | 307 | 308 | ##-------------------------------- 定义 jd_jdzz 是否静默运行(选填) -------------------------------- ## 309 | ## 控制京东赚赚是否静默运行,"false" 为否(默认值 "false",发送推送通知消息),默认每月1日推送一次通知,true 为是(即:不发送推送通知消息) 310 | export JDZZ_NOTIFY_CONTROL="" 311 | 312 | 313 | ##-------------------------------- 定义是否自动加购物车(选填) --------------------------------## 314 | ## 口袋书店和东东小窝有些任务需要将商品加进购物车才能完成,默认 "false" 不做这些任务,如想做,请设置为 "true" 315 | export PURCHASE_SHOPS="" 316 | 317 | 318 | ##-------------------------------- 定义取关参数(选填) --------------------------------## 319 | ## jd_unsubscribe 这个任务是用来取关每天做任务关注的商品和店铺,默认在每次运行时取关 20 个商品和 20 个店铺 320 | ## 如果取关数量不够,可以根据情况增加,还可以设置 jdUnsubscribeStopGoods 和 jdUnsubscribeStopShop 321 | ## 商品取关数量 322 | goodPageSize="" 323 | ## 店铺取关数量 324 | shopPageSize="" 325 | ## 遇到此商品不再取关此商品以及它后面的商品,需去商品详情页长按拷贝商品信息 326 | jdUnsubscribeStopGoods="" 327 | ## 遇到此店铺不再取关此店铺以及它后面的店铺,请从头开始输入店铺名称 328 | jdUnsubscribeStopShop="" 329 | 330 | 331 | ##-------------------------------- 互助码填法示例 --------------------------------## 332 | ## 互助码是填在 My 系列变量中的,ForOther 系统变量中只要填入 My 系列的变量名即可,按注释中的例子拼接,以东东农场为例,如下所示。 333 | ## 实际上东东农场一个账号只能给别人助力 3 次,多写的话,只有前几个会被助力。但如果前面的账号获得的助力次数已经达到上限了,那么还是会尝试继续给余下的账号助力,所以多填也是有意义的。 334 | ## ForOther 系列变量必须从 1 开始编号,依次编下去。 335 | 336 | # MyFruit1="e6e04602d5e343258873af1651b603ec" # 这是Cookie1这个账号的互助码 337 | # MyFruit2="52801b06ce2a462f95e1d59d7e856ef4" # 这是Cookie2这个账号的互助码 338 | # MyFruit3="e2fd1311229146cc9507528d0b054da8" # 这是Cookie3这个账号的互助码 339 | # MyFruit4="6dc9461f662d490991a31b798f624128" # 这是Cookie4这个账号的互助码 340 | # MyFruitA="5bc73a365ff74a559bdee785ea97fcc5" # 这是我和别人交换互助,另外一个用户A的互助码 341 | # MyFruitB="6d402dcfae1043fba7b519e0d6579a6f" # 这是我和别人交换互助,另外一个用户B的互助码 342 | 343 | # ForOtherFruit1="${MyFruit2}@${MyFruit4}@${MyFruitB}" # Cookie1 这个账号依次助力 Cookie2 的账号、Cookie4 的账号以及用户 B 344 | # ForOtherFruit2="${MyFruit1}@${MyFruitA}@${MyFruit4}" # Cookie2 这个账号依次助力 Cookie1 的账号的账号、用户 A 的账号以及 Cookie4 的账号 345 | # ForOtherFruit3="${MyFruit1}@${MyFruit2}@${MyFruit4}@${MyFruitA}@${MyFruitB}" # 解释同上,东东农场实际上只能助力 3 次 346 | # ForOtherFruit4="${MyFruit1}@${MyFruit2}@${MyFruit3}@${MyFruitA}@${MyFruitB}" # 解释同上,东东农场实际上只能助力 3 次 347 | 348 | 349 | ##-------------------------------- 定义获取共享池助力码个数(选填) --------------------------------## 350 | ## 对于需要助力的活动,js 脚本会从云端共享池随机取 HelpPoolNum 个共享码放到您固定的互助码后面,不会影响已有固定互助 351 | ## 你也可以提交自己的互助码到共享池,具体信息请访问 TG Bot https://t.me/TuringLabbot,并按提示操作 352 | ## 取值范围 0 到 25,不在范围内则为 0,默认为 0 353 | HelpPoolNum="" 354 | 355 | 356 | ##-------------------------------- 定义导出互助码模板样式(选填) --------------------------------## 357 | ## 定义 export_sharecodes.sh 导出的互助码模板样式,目前预定义三种模板,其他模板待开发。 358 | ## 不填则默认按“普通优先级助力模板”导出,Cookie 编号在前的优先助力 359 | ## 填 0 将使用“统一优先级助力模板”,所有账户要助力的码全部一致 360 | ## 填 1 使用“均匀助力模板”,所有账户获得助力次数一致 361 | HelpType="" 362 | 363 | 364 | ##-------------------------------- 互助码填写区域(选填) --------------------------------## 365 | 366 | ## 东东农场 367 | MyFruit1="" 368 | MyFruit2="" 369 | MyFruit3="" 370 | 371 | ForOtherFruit1="" 372 | ForOtherFruit2="" 373 | ForOtherFruit3="" 374 | 375 | 376 | ## 东东萌宠 377 | MyPet1="" 378 | MyPet2="" 379 | MyPet3="" 380 | 381 | ForOtherPet1="" 382 | ForOtherPet2="" 383 | ForOtherPet3="" 384 | 385 | 386 | ## 种豆得豆 387 | MyBean1="" 388 | MyBean2="" 389 | MyBean3="" 390 | 391 | ForOtherBean1="" 392 | ForOtherBean2="" 393 | ForOtherBean3="" 394 | 395 | 396 | ## 京喜工厂 397 | MyDreamFactory1="" 398 | MyDreamFactory2="" 399 | MyDreamFactory3="" 400 | 401 | ForOtherDreamFactory1="" 402 | ForOtherDreamFactory2="" 403 | ForOtherDreamFactory3="" 404 | 405 | 406 | ## 东东工厂 407 | MyJdFactory1="" 408 | MyJdFactory2="" 409 | MyJdFactory3="" 410 | 411 | ForOtherJdFactory1="" 412 | ForOtherJdFactory2="" 413 | ForOtherJdFactory3="" 414 | 415 | 416 | ## 疯狂的 JOY 417 | MyJoy1="" 418 | MyJoy2="" 419 | MyJoy3="" 420 | 421 | ForOtherJoy1="" 422 | ForOtherJoy2="" 423 | ForOtherJoy3="" 424 | 425 | 426 | ## 京东赚赚 427 | MyJdzz1="" 428 | MyJdzz2="" 429 | MyJdzz3="" 430 | 431 | ForOtherJdzz1="" 432 | ForOtherJdzz2="" 433 | ForOtherJdzz3="" 434 | 435 | 436 | ## 京喜农场 437 | ## 助力码格式如下: 438 | ## {"smp":"22bdadsfaadsfadse8a","active":"jdnc_1_btorange210113_2","joinnum":"1"} 439 | ## 注意:京喜农场 种植种子发生变化的时候,互助码也会变!! 440 | MyJxnc1='' 441 | MyJxnc2='' 442 | MyJxnc3='' 443 | 444 | ForOtherJxnc1="" 445 | ForOtherJxnc2="" 446 | ForOtherJxnc3="" 447 | 448 | 449 | ## 口袋书店 450 | MyBookShop1="" 451 | MyBookShop2="" 452 | MyBookShop3="" 453 | 454 | ForOtherBookShop1="" 455 | ForOtherBookShop2="" 456 | ForOtherBookShop3="" 457 | 458 | 459 | ## 签到领现金 460 | MyCash1="" 461 | MyCash2="" 462 | MyCash3="" 463 | 464 | ForOtherCash1="" 465 | ForOtherCash2="" 466 | ForOtherCash3="" 467 | 468 | 469 | ## 闪购盲盒 470 | MySgmh1="" 471 | MySgmh2="" 472 | MySgmh3="" 473 | 474 | ForOtherSgmh1="" 475 | ForOtherSgmh2="" 476 | ForOtherSgmh3="" 477 | 478 | 479 | ## 京喜财富岛 480 | MyCfd1="" 481 | MyCfd2="" 482 | MyCfd3="" 483 | 484 | ForOtherCfd1="" 485 | ForOtherCfd2="" 486 | ForOtherCfd3="" 487 | 488 | 489 | ## 环球挑战赛 490 | MyGlobal1="" 491 | MyGlobal2="" 492 | MyGlobal3="" 493 | 494 | ForOtherGlobal1="" 495 | ForOtherGlobal2="" 496 | ForOtherGlobal3="" 497 | 498 | -------------------------------------------------------------------------------- /sample/crontab.list.sample: -------------------------------------------------------------------------------- 1 | ENV_PATH= 2 | 3 | 55 8-23/3 * * * bash MY_PATH/git_pull.sh >> MY_PATH/log/git_pull.log 2>&1 4 | 57 3 * * * bash MY_PATH/rm_log.sh >/dev/null 2>&1 5 | 48 4 * * * bash MY_PATH/export_sharecodes.sh 6 | 33 13 * * * bash MY_PATH/jd.sh hangup 7 | 8 | # 运行 jd_scripts 脚本,仅列出长期任务作初始化用,AutoAddCron=true时,将自动添加短期任务。 9 | # 非 RikudouPatrickstar/jd_scripts 仓库中的脚本不能以 “jd_”、“jr_”、“jx_” 开头。 10 | # 请在最后保留一个空行。 11 | 12 | 5 0 * * * bash MY_PATH/jd.sh jd_bean_change 13 | 33 4 * * * bash MY_PATH/jd.sh jd_bean_home 14 | 3 0,18 * * * bash MY_PATH/jd.sh jd_bean_sign 15 | 5 7,12,19 * * * bash MY_PATH/jd.sh jd_beauty 16 | 0,30 0 * * * bash MY_PATH/jd.sh jd_blueCoin 17 | 7 8,12,18 * * * bash MY_PATH/jd.sh jd_bookshop 18 | 0 0 * * * bash MY_PATH/jd.sh jd_car 19 | 0 0 * * * bash MY_PATH/jd.sh jd_car_exchange 20 | 27 */4 * * * bash MY_PATH/jd.sh jd_cash 21 | 5 0,8,13,19 * * * bash MY_PATH/jd.sh jd_cfd 22 | 0 0 * * * bash MY_PATH/jd.sh jd_club_lottery 23 | 10 7,23 * * * bash MY_PATH/jd.sh jd_crazy_joy 24 | 10 12 * * * bash MY_PATH/jd.sh jd_crazy_joy_bonus 25 | 18 * * * * bash MY_PATH/jd.sh jd_daily_egg 26 | # 20 9 * * 6 bash MY_PATH/jd.sh jd_delCoupon 27 | 20 * * * * bash MY_PATH/jd.sh jd_dreamFactory 28 | */20 * * * * bash MY_PATH/jd.sh jd_family 29 | 5 6-18/6 * * * bash MY_PATH/jd.sh jd_fruit 30 | 47 7 * * * bash MY_PATH/jd.sh jd_get_share_code 31 | 36 * * * * bash MY_PATH/jd.sh jd_jdfactory 32 | 10 11 * * * bash MY_PATH/jd.sh jd_jdzz 33 | 45 */2,23 * * * bash MY_PATH/jd.sh jd_joy 34 | 15 */1 * * * bash MY_PATH/jd.sh jd_joy_feedPets 35 | 0 0-16/8 * * * bash MY_PATH/jd.sh jd_joy_reward 36 | 10 9-20/2 * * * bash MY_PATH/jd.sh jd_joy_run 37 | 0 0-10/2 * * * bash MY_PATH/jd.sh jd_joy_steal 38 | 20 5,23 * * * bash MY_PATH/jd.sh jd_jxd 39 | 0 9,12,18 * * * bash MY_PATH/jd.sh jd_jxnc 40 | 23 1 * * * bash MY_PATH/jd.sh jd_kd 41 | 10-20/5 12,23 * * * bash MY_PATH/jd.sh jd_live 42 | 11 1 * * * bash MY_PATH/jd.sh jd_lotteryMachine 43 | 0 */2 * * * bash MY_PATH/jd.sh jd_moneyTree 44 | 10 7 * * * bash MY_PATH/jd.sh jd_ms 45 | 20 0,20 * * * bash MY_PATH/jd.sh jd_necklace 46 | 5 6-18/6 * * * bash MY_PATH/jd.sh jd_pet 47 | 12 * * * * bash MY_PATH/jd.sh jd_pigPet 48 | 0 7-22/1 * * * bash MY_PATH/jd.sh jd_plantBean 49 | # 1 0,23 * * * bash MY_PATH/jd.sh jd_price 50 | 11 9 * * * bash MY_PATH/jd.sh jd_rankingList 51 | 1 1 * * * bash MY_PATH/jd.sh jd_redPacket 52 | 27 8 * * * bash MY_PATH/jd.sh jd_sgmh 53 | 10 0 * * * bash MY_PATH/jd.sh jd_shop 54 | 16 6,23 * * * bash MY_PATH/jd.sh jd_small_home 55 | 8 */3 * * * bash MY_PATH/jd.sh jd_speed 56 | 1 1,6 * * * bash MY_PATH/jd.sh jd_speed_sign 57 | 11 1-23/5 * * * bash MY_PATH/jd.sh jd_superMarket 58 | 36 8,18 * * * bash MY_PATH/jd.sh jd_syj 59 | 55 23 * * * bash MY_PATH/jd.sh jd_unsubscribe 60 | 39 7 * * * bash MY_PATH/jd.sh jx_sign 61 | 62 | # 以下为自动添加的任务 63 | -------------------------------------------------------------------------------- /thanks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikudouPatrickstar/jd-base/4506a5cd116ebfcc79f38de59d525b179e0946db/thanks.png -------------------------------------------------------------------------------- /update.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lxk0301 https://gitee.com/lxk0301 3 | * @Date: 2020-12-20 13:50:34 4 | * @Last Modified by: lxk0301 5 | * @Last Modified time: 2020-12-20 13:51:02 6 | */ 7 | const $ = new Env('通知'); 8 | const notify = require('./scripts/sendNotify'); 9 | const fs = require('fs'); 10 | !(async() => { 11 | await update(); 12 | })() 13 | .catch((e) => $.logErr(e)) 14 | .finally(() => $.done()) 15 | 16 | async function update() { 17 | try { 18 | if (fs.existsSync('new_task')) { 19 | const newTaskContent = await fs.readFileSync('./new_task', 'utf8'); 20 | if (newTaskContent) { 21 | await notify.sendNotify('新增薅羊毛任务通知', newTaskContent); 22 | } 23 | } 24 | 25 | if (fs.existsSync('drop_task')) { 26 | const dropTaskContent = await fs.readFileSync('./drop_task', 'utf8'); 27 | if (dropTaskContent) { 28 | await notify.sendNotify('删除失效任务通知', dropTaskContent); 29 | } 30 | } 31 | 32 | if (fs.existsSync('version')) { 33 | const versionContent = await fs.readFileSync('./version', 'utf8'); 34 | if (versionContent) { 35 | await notify.sendNotify('配置文件更新通知', versionContent); 36 | } 37 | } 38 | } catch (err) { 39 | console.error(err) 40 | } 41 | } 42 | function Env(t,e){class s{constructor(t){this.env=t}send(t,e="GET"){t="string"==typeof t?{url:t}:t;let s=this.get;return"POST"===e&&(s=this.post),new Promise((e,i)=>{s.call(this,t,(t,s,r)=>{t?i(t):e(s)})})}get(t){return this.send.call(this.env,t)}post(t){return this.send.call(this.env,t,"POST")}}return new class{constructor(t,e){this.name=t,this.http=new s(this),this.data=null,this.dataFile="box.dat",this.logs=[],this.isMute=!1,this.isNeedRewrite=!1,this.logSeparator="\n",this.startTime=(new Date).getTime(),Object.assign(this,e),this.log("",`\ud83d\udd14${this.name}, \u5f00\u59cb!`)}isNode(){return"undefined"!=typeof module&&!!module.exports}isQuanX(){return"undefined"!=typeof $task}isSurge(){return"undefined"!=typeof $httpClient&&"undefined"==typeof $loon}isLoon(){return"undefined"!=typeof $loon}toObj(t,e=null){try{return JSON.parse(t)}catch{return e}}toStr(t,e=null){try{return JSON.stringify(t)}catch{return e}}getjson(t,e){let s=e;const i=this.getdata(t);if(i)try{s=JSON.parse(this.getdata(t))}catch{}return s}setjson(t,e){try{return this.setdata(JSON.stringify(t),e)}catch{return!1}}getScript(t){return new Promise(e=>{this.get({url:t},(t,s,i)=>e(i))})}runScript(t,e){return new Promise(s=>{let i=this.getdata("@chavy_boxjs_userCfgs.httpapi");i=i?i.replace(/\n/g,"").trim():i;let r=this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout");r=r?1*r:20,r=e&&e.timeout?e.timeout:r;const[o,h]=i.split("@"),a={url:`http://${h}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:r},headers:{"X-Key":o,Accept:"*/*"}};this.post(a,(t,e,i)=>s(i))}).catch(t=>this.logErr(t))}loaddata(){if(!this.isNode())return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e);if(!s&&!i)return{};{const i=s?t:e;try{return JSON.parse(this.fs.readFileSync(i))}catch(t){return{}}}}}writedata(){if(this.isNode()){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e),r=JSON.stringify(this.data);s?this.fs.writeFileSync(t,r):i?this.fs.writeFileSync(e,r):this.fs.writeFileSync(t,r)}}lodash_get(t,e,s){const i=e.replace(/\[(\d+)\]/g,".$1").split(".");let r=t;for(const t of i)if(r=Object(r)[t],void 0===r)return s;return r}lodash_set(t,e,s){return Object(t)!==t?t:(Array.isArray(e)||(e=e.toString().match(/[^.[\]]+/g)||[]),e.slice(0,-1).reduce((t,s,i)=>Object(t[s])===t[s]?t[s]:t[s]=Math.abs(e[i+1])>>0==+e[i+1]?[]:{},t)[e[e.length-1]]=s,t)}getdata(t){let e=this.getval(t);if(/^@/.test(t)){const[,s,i]=/^@(.*?)\.(.*?)$/.exec(t),r=s?this.getval(s):"";if(r)try{const t=JSON.parse(r);e=t?this.lodash_get(t,i,""):e}catch(t){e=""}}return e}setdata(t,e){let s=!1;if(/^@/.test(e)){const[,i,r]=/^@(.*?)\.(.*?)$/.exec(e),o=this.getval(i),h=i?"null"===o?null:o||"{}":"{}";try{const e=JSON.parse(h);this.lodash_set(e,r,t),s=this.setval(JSON.stringify(e),i)}catch(e){const o={};this.lodash_set(o,r,t),s=this.setval(JSON.stringify(o),i)}}else s=this.setval(t,e);return s}getval(t){return this.isSurge()||this.isLoon()?$persistentStore.read(t):this.isQuanX()?$prefs.valueForKey(t):this.isNode()?(this.data=this.loaddata(),this.data[t]):this.data&&this.data[t]||null}setval(t,e){return this.isSurge()||this.isLoon()?$persistentStore.write(t,e):this.isQuanX()?$prefs.setValueForKey(t,e):this.isNode()?(this.data=this.loaddata(),this.data[e]=t,this.writedata(),!0):this.data&&this.data[e]||null}initGotEnv(t){this.got=this.got?this.got:require("got"),this.cktough=this.cktough?this.cktough:require("tough-cookie"),this.ckjar=this.ckjar?this.ckjar:new this.cktough.CookieJar,t&&(t.headers=t.headers?t.headers:{},void 0===t.headers.Cookie&&void 0===t.cookieJar&&(t.cookieJar=this.ckjar))}get(t,e=(()=>{})){t.headers&&(delete t.headers["Content-Type"],delete t.headers["Content-Length"]),this.isSurge()||this.isLoon()?(this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient.get(t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)})):this.isQuanX()?(this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>e(t))):this.isNode()&&(this.initGotEnv(t),this.got(t).on("redirect",(t,e)=>{try{if(t.headers["set-cookie"]){const s=t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString();this.ckjar.setCookieSync(s,null),e.cookieJar=this.ckjar}}catch(t){this.logErr(t)}}).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)}))}post(t,e=(()=>{})){if(t.body&&t.headers&&!t.headers["Content-Type"]&&(t.headers["Content-Type"]="application/x-www-form-urlencoded"),t.headers&&delete t.headers["Content-Length"],this.isSurge()||this.isLoon())this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient.post(t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)});else if(this.isQuanX())t.method="POST",this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>e(t));else if(this.isNode()){this.initGotEnv(t);const{url:s,...i}=t;this.got.post(s,i).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)})}}time(t){let e={"M+":(new Date).getMonth()+1,"d+":(new Date).getDate(),"H+":(new Date).getHours(),"m+":(new Date).getMinutes(),"s+":(new Date).getSeconds(),"q+":Math.floor(((new Date).getMonth()+3)/3),S:(new Date).getMilliseconds()};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,((new Date).getFullYear()+"").substr(4-RegExp.$1.length)));for(let s in e)new RegExp("("+s+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?e[s]:("00"+e[s]).substr((""+e[s]).length)));return t}msg(e=t,s="",i="",r){const o=t=>{if(!t)return t;if("string"==typeof t)return this.isLoon()?t:this.isQuanX()?{"open-url":t}:this.isSurge()?{url:t}:void 0;if("object"==typeof t){if(this.isLoon()){let e=t.openUrl||t.url||t["open-url"],s=t.mediaUrl||t["media-url"];return{openUrl:e,mediaUrl:s}}if(this.isQuanX()){let e=t["open-url"]||t.url||t.openUrl,s=t["media-url"]||t.mediaUrl;return{"open-url":e,"media-url":s}}if(this.isSurge()){let e=t.url||t.openUrl||t["open-url"];return{url:e}}}};this.isMute||(this.isSurge()||this.isLoon()?$notification.post(e,s,i,o(r)):this.isQuanX()&&$notify(e,s,i,o(r)));let h=["","==============\ud83d\udce3\u7cfb\u7edf\u901a\u77e5\ud83d\udce3=============="];h.push(e),s&&h.push(s),i&&h.push(i),console.log(h.join("\n")),this.logs=this.logs.concat(h)}log(...t){t.length>0&&(this.logs=[...this.logs,...t]),console.log(t.join(this.logSeparator))}logErr(t,e){const s=!this.isSurge()&&!this.isQuanX()&&!this.isLoon();s?this.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t.stack):this.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t)}wait(t){return new Promise(e=>setTimeout(e,t))}done(t={}){const e=(new Date).getTime(),s=(e-this.startTime)/1e3;this.log("",`\ud83d\udd14${this.name}, \u7ed3\u675f! \ud83d\udd5b ${s} \u79d2`),this.log(),(this.isSurge()||this.isQuanX()||this.isLoon())&&$done(t)}}(t,e)} --------------------------------------------------------------------------------