From f1f8ece8a20025470d3b702e52b82829dbd5bd94 Mon Sep 17 00:00:00 2001 From: whyour Date: Sun, 14 Mar 2021 22:06:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 16 + .gitignore | 21 ++ .prettierignore | 8 + .prettierrc | 11 + .umirc.ts | 18 + README.md | 27 ++ csv.sh | 24 ++ export_sharecodes.sh | 143 ++++++++ git_pull.sh | 372 ++++++++++++++++++++ jd.sh | 281 +++++++++++++++ package.json | 44 +++ rm_log.sh | 57 +++ sample/auth.json | 1 + sample/config.sh.sample | 636 ++++++++++++++++++++++++++++++++++ src/assets/logo.png | Bin 0 -> 15995 bytes src/layouts/defaultProps.tsx | 81 +++++ src/layouts/index.less | 23 ++ src/layouts/index.tsx | 54 +++ src/pages/code/index.less | 0 src/pages/code/index.tsx | 93 +++++ src/pages/config/index.less | 0 src/pages/config/index.tsx | 148 ++++++++ src/pages/cookie/index.less | 0 src/pages/cookie/index.tsx | 190 ++++++++++ src/pages/crontab/index.less | 0 src/pages/crontab/index.tsx | 92 +++++ src/pages/diff/index.less | 12 + src/pages/diff/index.tsx | 93 +++++ src/pages/diy/index.less | 0 src/pages/diy/index.tsx | 92 +++++ src/pages/log/index.less | 0 src/pages/log/index.tsx | 112 ++++++ src/pages/login/index.less | 58 ++++ src/pages/login/index.tsx | 73 ++++ src/pages/password/index.less | 0 src/pages/password/index.tsx | 84 +++++ src/utils/config.ts | 37 ++ src/utils/http.ts | 29 ++ tsconfig.json | 37 ++ typings.d.ts | 8 + update.js | 42 +++ 41 files changed, 3017 insertions(+) create mode 100755 .editorconfig create mode 100644 .gitignore create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 .umirc.ts create mode 100644 README.md create mode 100644 csv.sh create mode 100755 export_sharecodes.sh create mode 100755 git_pull.sh create mode 100755 jd.sh create mode 100644 package.json create mode 100755 rm_log.sh create mode 100755 sample/auth.json create mode 100755 sample/config.sh.sample create mode 100644 src/assets/logo.png create mode 100644 src/layouts/defaultProps.tsx create mode 100644 src/layouts/index.less create mode 100644 src/layouts/index.tsx create mode 100644 src/pages/code/index.less create mode 100644 src/pages/code/index.tsx create mode 100644 src/pages/config/index.less create mode 100644 src/pages/config/index.tsx create mode 100644 src/pages/cookie/index.less create mode 100644 src/pages/cookie/index.tsx create mode 100644 src/pages/crontab/index.less create mode 100644 src/pages/crontab/index.tsx create mode 100644 src/pages/diff/index.less create mode 100644 src/pages/diff/index.tsx create mode 100644 src/pages/diy/index.less create mode 100644 src/pages/diy/index.tsx create mode 100644 src/pages/log/index.less create mode 100644 src/pages/log/index.tsx create mode 100644 src/pages/login/index.less create mode 100644 src/pages/login/index.tsx create mode 100644 src/pages/password/index.less create mode 100644 src/pages/password/index.tsx create mode 100644 src/utils/config.ts create mode 100644 src/utils/http.ts create mode 100644 tsconfig.json create mode 100644 typings.d.ts create mode 100755 update.js diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 00000000000..7e3649acc2c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..bfb0df96cc1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/npm-debug.log* +/yarn-error.log +/yarn.lock +/package-lock.json + +# production +/dist + +# misc +.DS_Store + +# umi +/src/.umi +/src/.umi-production +/src/.umi-test +/.env.local +/config diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000000..0d4222f5443 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +**/*.md +**/*.svg +**/*.ejs +**/*.html +package.json +.umi +.umi-production +.umi-test diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..94beb148408 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "printWidth": 80, + "overrides": [ + { + "files": ".prettierrc", + "options": { "parser": "json" } + } + ] +} diff --git a/.umirc.ts b/.umirc.ts new file mode 100644 index 00000000000..bb9e827816c --- /dev/null +++ b/.umirc.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'umi'; +import { SmileOutlined, CrownOutlined, TabletOutlined, AntDesignOutlined } from '@ant-design/icons'; + +export default defineConfig({ + layout: false, + locale: {}, + nodeModulesTransform: { + type: 'none', + }, + fastRefresh: {}, + favicon: 'http://demo.sc.chinaz.com/Files/pic/iconsico/8002/g5.ico', + proxy: { + '/api': { + 'target': 'http://127.0.0.1:5678/', + 'changeOrigin': true, + }, + }, +}); diff --git a/README.md b/README.md new file mode 100644 index 00000000000..7e7d5b85fee --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +## 青龙(WIP) + +青龙,又名苍龙,在中国传统文化中是四象之一、[天之四灵](https://zh.wikipedia.org/wiki/%E5%A4%A9%E4%B9%8B%E5%9B%9B%E7%81%B5)之一,根据五行学说,它是代表东方的灵兽,为青色的龙,五行属木,代表的季节是春季,八卦主震。苍龙与应龙一样,都是身具羽翼。《张果星宗》称“又有辅翼,方为真龙”。 + +《后汉书·律历志下》记载:日周于天,一寒一暑,四时备成,万物毕改,摄提迁次,青龙移辰,谓之岁。 + +在中国[二十八宿](https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%8D%81%E5%85%AB%E5%AE%BF)中,青龙是东方七宿(角、亢、氐、房、心、尾、箕)的总称。 在早期星宿信仰中,祂是最尊贵的天神[1]。 但被道教信仰吸纳入其神系后,神格大跌,道教将其称为“孟章”,在不同的道经中有“帝君”、“圣将”、“神将”和“捕鬼将”等称呼[2],与白虎监兵神君一起,是道教的护卫天神。 + +## 多谢 + +本仓库部分shell脚本及其配置参考自[nevinee](https://github.com/nevinee)大佬, [https://github.com/nevinee/jd_shell](https://github.com/nevinee/jd_shell), 感谢👍👍👍 + +## 免责声明 + +1. 此仓储脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。 + +2. 由于此仓储脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。 + +3. 请勿将此仓储脚本用于任何商业或非法目的,若违反规定请自行对此负责。 + +4. 此仓储脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。 + +5. 本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。 + +6. 如果任何单位或个人认为此仓储脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此仓储脚本。 + +7. 所有直接或间接使用、查看此仓储脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此仓储脚本,即视为您已接受此免责声明。 diff --git a/csv.sh b/csv.sh new file mode 100644 index 00000000000..ed8488ee330 --- /dev/null +++ b/csv.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +## 判断环境 +ShellDir=${JD_DIR:-$(cd $(dirname $0); pwd)} +LogDir=${ShellDir}/log +Income=${LogDir}/bean_income.csv +Outlay=${LogDir}/bean_outlay.csv + +## 执行 +cd ${LogDir}/jd_bean_change +for log in $(ls); do + LogDate=$(echo ${log} | cut -c1-10) + BeanDate=$(date "+%Y-%m-%d" -d "1 day ago ${LogDate}") + + if [[ -z $(grep "${BeanDate}" ${Income}) ]]; then + echo -n "${BeanDate}," >> ${Income} + grep -E "昨日收入" ${log} | grep -oE "\d+" | perl -0777 -pe "s|\n(\d+)|,\1|g" >> ${Income} + fi + + if [[ -z $(grep "${BeanDate}" ${Outlay}) ]]; then + echo -n "${BeanDate}," >> ${Outlay} + grep -E "昨日支出" ${log} | grep -oE "\d+" | perl -0777 -pe "s|\n(\d+)|,\1|g" >> ${Outlay} + fi +done diff --git a/export_sharecodes.sh b/export_sharecodes.sh new file mode 100755 index 00000000000..2d32145d096 --- /dev/null +++ b/export_sharecodes.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash + +## 路径、环境判断 +ShellDir=${JD_DIR:-$(cd $(dirname $0); pwd)} +LogDir=${ShellDir}/log +ConfigDir=${ShellDir}/config +FileConf=${ConfigDir}/config.sh +[[ ${ANDROID_RUNTIME_ROOT}${ANDROID_ROOT} ]] && Opt="P" || Opt="E" +Tips="从日志中未找到任何互助码" + +## 所有有互助码的活动,只需要把脚本名称去掉前缀 jd_ 后列在 Name1 中,将其中文名称列在 Name2 中,对应 config.sh 中互助码后缀列在 Name3 中即可。 +## Name1、Name2 和 Name3 中的三个名称必须一一对应。 +Name1=(fruit pet plantBean dreamFactory jdfactory crazy_joy jdzz jxnc bookshop cash sgmh cfd global) +Name2=(东东农场 东东萌宠 京东种豆得豆 京喜工厂 东东工厂 crazyJoy任务 京东赚赚 京喜农场 口袋书店 签到领现金 闪购盲盒 京喜财富岛 环球挑战赛) +Name3=(Fruit Pet Bean DreamFactory JdFactory Joy Jdzz Jxnc BookShop Cash Sgmh Cfd Global) + + +## 导入 config.sh +function Import_Conf { + if [ -f ${FileConf} ] + then + . ${FileConf} + if [ -z "${Cookie1}" ]; then + echo -e "请先在 config.sh 中配置好 Cookie\n" + exit 1 + fi + else + echo -e "配置文件 ${FileConf} 不存在,请先按教程配置好该文件\n" + exit 1 + fi +} + + +## 用户数量 UserSum +function Count_UserSum { + for ((i=1; i<=1000; i++)); do + Tmp=Cookie$i + CookieTmp=${!Tmp} + [[ ${CookieTmp} ]] && UserSum=$i || break + done +} + + +## 导出互助码的通用程序 +function Cat_Scodes { + if [ -d ${LogDir}/jd_$1 ] && [[ $(ls ${LogDir}/jd_$1) != "" ]]; then + cd ${LogDir}/jd_$1 + + ## 导出助力码变量(My) + for log in $(ls -r); do + case $# in + 2) + codes=$(cat ${log} | grep -${Opt} "开始【京东账号|您的(好友)?助力码为" | uniq | perl -0777 -pe "{s|\*||g; s|开始||g; s|\n您的(好友)?助力码为(:)?:?|:|g; s|,.+||g}" | sed -r "s/【京东账号/My$2/;s/】.*?:/='/;s/】.*?/='/;s/$/'/;s/\(每次运行都变化,不影响\)//") + ;; + 3) + codes=$(grep -${Opt} $3 ${log} | uniq | sed -r "s/【京东账号/My$2/;s/(.*?】/='/;s/$/'/") + ;; + esac + if [[ ${codes} ]]; then + ## 添加判断,若未找到该用户互助码,则设置为空值 + for ((user_num=1;user_num<=${UserSum};user_num++)); do + echo -e "${codes}" | grep -${Opt}q "My$2${user_num}=" + if [ $? -eq 1 ]; then + if [ $user_num == 1 ]; then + codes=$(echo "${codes}" | sed -r "1i My${2}1=''") + else + codes=$(echo "${codes}" | sed -r "/My$2$(expr ${user_num} - 1)=/a\My$2${user_num}=''") + fi + fi + done + break + fi + done + + ## 导出为他人助力变量(ForOther) + if [[ ${codes} ]]; then + help_code="" + for ((user_num=1;user_num<=${UserSum};user_num++)); do + echo -e "${codes}" | grep -${Opt}q "My$2${user_num}=''" + if [ $? -eq 1 ]; then + help_code=${help_code}"\${My"$2${user_num}"}@" + fi + done + ## 生成互助规则模板 + for_other_codes="" + case $HelpType in + 0) ### 统一优先级助力模板 + new_code=$(echo ${help_code} | sed "s/@$//") + for ((user_num=1;user_num<=${UserSum};user_num++)); do + if [ $user_num == 1 ]; then + for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\""${new_code}"\"\n" + else + for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\"\${ForOther"${2}1"}\"\n" + fi + done + ;; + 1) ### 均匀助力模板 + for ((user_num=1;user_num<=${UserSum};user_num++)); do + echo ${help_code} | grep "\${My"$2${user_num}"}@" > /dev/null + if [ $? -eq 0 ]; then + left_str=$(echo ${help_code} | sed "s/\${My$2${user_num}}@/ /g" | awk '{print $1}') + right_str=$(echo ${help_code} | sed "s/\${My$2${user_num}}@/ /g" | awk '{print $2}') + mark="\${My$2${user_num}}@" + else + left_str=$(echo ${help_code} | sed "s/${mark}/ /g" | awk '{print $1}')${mark} + right_str=$(echo ${help_code} | sed "s/${mark}/ /g" | awk '{print $2}') + fi + new_code=$(echo ${right_str}${left_str} | sed "s/@$//") + for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\""${new_code}"\"\n" + done + ;; + *) ### 普通优先级助力模板 + for ((user_num=1;user_num<=${UserSum};user_num++)); do + new_code=$(echo ${help_code} | sed "s/\${My"$2${user_num}"}@//;s/@$//") + for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\""${new_code}"\"\n" + done + ;; + esac + echo -e "${codes}\n\n${for_other_codes}" | sed s/[[:space:]]//g + else + echo ${Tips} + fi + else + echo "未运行过 jd_$1 脚本,未产生日志" + fi +} + + +## 汇总 +function Cat_All { + echo -e "\n从最后一个日志提取互助码,受日志内容影响,仅供参考。" + for ((i=0; i<${#Name1[*]}; i++)); do + echo -e "\n${Name2[i]}:" + [[ $(Cat_Scodes "${Name1[i]}" "${Name3[i]}" "的${Name2[i]}好友互助码") == ${Tips} ]] && Cat_Scodes "${Name1[i]}" "${Name3[i]}" || Cat_Scodes "${Name1[i]}" "${Name3[i]}" "的${Name2[i]}好友互助码" + done +} + + +## 执行并写入日志 +LogTime=$(date "+%Y-%m-%d-%H-%M-%S") +LogFile="${LogDir}/export_sharecodes/${LogTime}.log" +[ ! -d "${LogDir}/export_sharecodes" ] && mkdir -p ${LogDir}/export_sharecodes +Import_Conf && Count_UserSum && Cat_All | perl -pe "{s|京东种豆|种豆|; s|crazyJoy任务|疯狂的JOY|}" | tee ${LogFile} diff --git a/git_pull.sh b/git_pull.sh new file mode 100755 index 00000000000..07ceedf857f --- /dev/null +++ b/git_pull.sh @@ -0,0 +1,372 @@ +#!/usr/bin/env bash + +## 文件路径、脚本网址、文件版本以及各种环境的判断 +ShellDir=${JD_DIR:-$(cd $(dirname $0); pwd)} +[[ ${JD_DIR} ]] && ShellJd=jd || ShellJd=${ShellDir}/jd.sh +LogDir=${ShellDir}/log +[ ! -d ${LogDir} ] && mkdir -p ${LogDir} +ScriptsDir=${ShellDir}/scripts +Scripts2Dir=${ShellDir}/scripts2 +ConfigDir=${ShellDir}/config +FileConf=${ConfigDir}/config.sh +FileDiy=${ConfigDir}/diy.sh +FileConfSample=${ShellDir}/sample/config.sh.sample +ListCron=${ConfigDir}/crontab.list +ListCronLxk=${ScriptsDir}/docker/crontab_list.sh +ListCronShylocks=${Scripts2Dir}/docker/crontab_list.sh +ListTask=${LogDir}/task.list +ListJs=${LogDir}/js.list +ListJsAdd=${LogDir}/js-add.list +ListJsDrop=${LogDir}/js-drop.list +ContentVersion=${ShellDir}/version +ContentNewTask=${ShellDir}/new_task +ContentDropTask=${ShellDir}/drop_task +SendCount=${ShellDir}/send_count +isTermux=${ANDROID_RUNTIME_ROOT}${ANDROID_ROOT} +ShellURL=${JD_SHELL_URL:-git@gitee.com:evine/jd_shell.git} +ScriptsURL=${JD_SCRIPTS_URL:-git@gitee.com:lxk0301/jd_scripts.git} + +## 导入配置文件 +function Import_Conf { + if [ -f ${FileConf} ]; then + . ${FileConf} + fi +} + +## 更新crontab,gitee服务器同一时间限制5个链接,因此每个人更新代码必须错开时间,每次执行git_pull随机生成。 +## 每天次数随机,更新时间随机,更新秒数随机,至少6次,至多12次,大部分为8-10次,符合正态分布。 +function Update_Cron { + if [[ $(date "+%-H") -le 2 ]] && [ -f ${ListCron} ]; then + RanMin=$((${RANDOM} % 60)) + RanSleep=$((${RANDOM} % 56)) + RanHourArray[0]=$((${RANDOM} % 3)) + for ((i=1; i<14; i++)); do + j=$(($i - 1)) + tmp=$((${RANDOM} % 3 + ${RanHourArray[j]} + 2)) + [[ ${tmp} -lt 24 ]] && RanHourArray[i]=${tmp} || break + done + RanHour=${RanHourArray[0]} + for ((i=1; i<${#RanHourArray[*]}; i++)); do + RanHour="${RanHour},${RanHourArray[i]}" + done + perl -i -pe "s|.+(bash.+git_pull.+log.*)|${RanMin} ${RanHour} \* \* \* sleep ${RanSleep} && \1|" ${ListCron} + crontab ${ListCron} + fi +} + +## 重置仓库remote url +function Reset_RepoUrl { + if [[ ${JD_DIR} ]] && [[ ${ENABLE_RESET_REPO_URL} == true ]]; then + if [ -d ${ShellDir}/.git ]; then + cd ${ShellDir} + git remote set-url origin ${ShellURL} + git reset --hard + fi + if [ -d ${ScriptsDir}/.git ]; then + cd ${ScriptsDir} + git remote set-url origin ${ScriptsURL} + git reset --hard + fi + fi +} + +## 更新shell +function Git_PullShell { + echo -e "更新shell...\n" + cd ${ShellDir} + git fetch --all + ExitStatusShell=$? + git reset --hard origin/master + echo +} + +## 更新shell成功后的操作 +function Git_PullShellNext { + if [[ ${ExitStatusShell} -eq 0 ]]; then + echo -e "更新shell成功...\n" + Update_Entrypoint + [[ "${PanelDependOld}" != "${PanelDependNew}" ]] && cd ${ShellDir}/panel && Npm_Install panel + cp -f ${FileConfSample} ${ConfigDir}/config.sh.sample + [ -d ${ScriptsDir}/node_modules ] && Notify_Version + else + echo -e "更新shell失败,请检查原因...\n" + fi +} + +## 克隆scripts +function Git_CloneScripts { + echo -e "克隆scripts...\n" + git clone -b master ${ScriptsURL} ${ScriptsDir} + ExitStatusScripts=$? + echo +} + +## 更新scripts +function Git_PullScripts { + echo -e "更新scripts...\n" + cd ${ScriptsDir} + git fetch --all + ExitStatusScripts=$? + git reset --hard origin/master + echo +} + +## 更新docker-entrypoint +function Update_Entrypoint { + if [[ ${JD_DIR} ]] && [[ $(cat ${ShellDir}/docker/docker-entrypoint.sh) != $(cat /usr/local/bin/docker-entrypoint.sh) ]]; then + cp -f ${ShellDir}/docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + chmod 777 /usr/local/bin/docker-entrypoint.sh + fi +} + +## 用户数量UserSum +function Count_UserSum { + i=1 + while [ $i -le 1000 ]; do + Tmp=Cookie$i + CookieTmp=${!Tmp} + [[ ${CookieTmp} ]] && UserSum=$i || break + let i++ + done +} + +## 检测文件:LXK9301/jd_scripts 仓库中的 docker/crontab_list.sh +## 检测定时任务是否有变化,此函数会在Log文件夹下生成四个文件,分别为: +## task.list crontab.list中的所有任务清单,仅保留脚本名 +## js.list 上述检测文件中用来运行js脚本的清单(去掉后缀.js,非运行脚本的不会包括在内) +## js-add.list 如果上述检测文件增加了定时任务,这个文件内容将不为空 +## js-drop.list 如果上述检测文件删除了定时任务,这个文件内容将不为空 +function Diff_Cron { + if [ -f ${ListCron} ]; then + if [ -n "${JD_DIR}" ] + then + grep -E " j[drx]_\w+" ${ListCron} | perl -pe "s|.+ (j[drx]_\w+).*|\1|" | sort -u > ${ListTask} + else + grep "${ShellDir}/" ${ListCron} | grep -E " j[drx]_\w+" | perl -pe "s|.+ (j[drx]_\w+).*|\1|" | sort -u > ${ListTask} + fi + cat ${ListCronLxk} | grep -E "j[drx]_\w+\.js" | perl -pe "s|.+(j[drx]_\w+)\.js.+|\1|" | sort -u > ${ListJs} + grep -vwf ${ListTask} ${ListJs} > ${ListJsAdd} + grep -vwf ${ListJs} ${ListTask} > ${ListJsDrop} + else + echo -e "${ListCron} 文件不存在,请先定义你自己的crontab.list...\n" + fi +} + +## 发送删除失效定时任务的消息 +function Notify_DropTask { + cd ${ShellDir} + node update.js + [ -f ${ContentDropTask} ] && rm -f ${ContentDropTask} +} + +## 发送新的定时任务消息 +function Notify_NewTask { + cd ${ShellDir} + node update.js + [ -f ${ContentNewTask} ] && rm -f ${ContentNewTask} +} + +## 检测配置文件版本 +function Notify_Version { + ## 识别出两个文件的版本号 + VerConfSample=$(grep " Version: " ${FileConfSample} | perl -pe "s|.+v((\d+\.?){3})|\1|") + [ -f ${FileConf} ] && VerConf=$(grep " Version: " ${FileConf} | perl -pe "s|.+v((\d+\.?){3})|\1|") + + ## 删除旧的发送记录文件 + [ -f "${SendCount}" ] && [[ $(cat ${SendCount}) != ${VerConfSample} ]] && rm -f ${SendCount} + + ## 识别出更新日期和更新内容 + UpdateDate=$(grep " Date: " ${FileConfSample} | awk -F ": " '{print $2}') + UpdateContent=$(grep " Update Content: " ${FileConfSample} | awk -F ": " '{print $2}') + + ## 如果是今天,并且版本号不一致,则发送通知 + if [ -f ${FileConf} ] && [[ "${VerConf}" != "${VerConfSample}" ]] && [[ ${UpdateDate} == $(date "+%Y-%m-%d") ]] + then + if [ ! -f ${SendCount} ]; then + echo -e "检测到配置文件config.sh.sample有更新\n\n更新日期: ${UpdateDate}\n当前版本: ${VerConf}\n新的版本: ${VerConfSample}\n更新内容: ${UpdateContent}\n更新说明: 如需使用新功能请对照config.sh.sample,将相关新参数手动增加到你自己的config.sh中,否则请无视本消息。本消息只在该新版本配置文件更新当天发送一次。" | tee ${ContentVersion} + cd ${ShellDir} + node update.js + if [ $? -eq 0 ]; then + echo "${VerConfSample}" > ${SendCount} + [ -f ${ContentVersion} ] && rm -f ${ContentVersion} + fi + fi + else + [ -f ${ContentVersion} ] && rm -f ${ContentVersion} + [ -f ${SendCount} ] && rm -f ${SendCount} + fi +} + +## npm install 子程序,判断是否为安卓,判断是否安装有yarn +function Npm_InstallSub { + if [ -n "${isTermux}" ] + then + npm install --no-bin-links || npm install --no-bin-links --registry=https://registry.npm.taobao.org + elif ! type yarn >/dev/null 2>&1 + then + npm install || npm install --registry=https://registry.npm.taobao.org + else + echo -e "检测到本机安装了 yarn,使用 yarn 替代 npm...\n" + yarn install || yarn install --registry=https://registry.npm.taobao.org + fi +} + +## npm install +function Npm_Install { + echo -e "检测到 $1 的依赖包有变化,运行 npm install...\n" + Npm_InstallSub + if [ $? -ne 0 ]; then + echo -e "\nnpm install 运行不成功,自动删除 $1/node_modules 后再次尝试一遍..." + rm -rf node_modules + fi + echo + + if [ ! -d node_modules ]; then + echo -e "运行 npm install...\n" + Npm_InstallSub + if [ $? -ne 0 ]; then + echo -e "\nnpm install 运行不成功,自动删除 $1/node_modules...\n" + echo -e "请进入 $1 目录后手动运行 npm install...\n" + echo -e "3...\n" + sleep 1 + echo -e "2...\n" + sleep 1 + echo -e "1...\n" + sleep 1 + rm -rf node_modules + fi + fi +} + +## 输出是否有新的定时任务 +function Output_ListJsAdd { + if [ -s ${ListJsAdd} ]; then + echo -e "检测到有新的定时任务:\n" + cat ${ListJsAdd} + echo + fi +} + +## 输出是否有失效的定时任务 +function Output_ListJsDrop { + if [ ${ExitStatusScripts} -eq 0 ] && [ -s ${ListJsDrop} ]; then + echo -e "检测到有失效的定时任务:\n" + cat ${ListJsDrop} + echo + fi +} + +## 自动删除失效的脚本与定时任务,需要5个条件:1.AutoDelCron 设置为 true;2.正常更新js脚本,没有报错;3.js-drop.list不为空;4.crontab.list存在并且不为空;5.已经正常运行过npm install +## 检测文件:LXK9301/jd_scripts 仓库中的 docker/crontab_list.sh +## 如果检测到某个定时任务在上述检测文件中已删除,那么在本地也删除对应定时任务 +function Del_Cron { + if [ "${AutoDelCron}" = "true" ] && [ -s ${ListJsDrop} ] && [ -s ${ListCron} ] && [ -d ${ScriptsDir}/node_modules ]; then + echo -e "开始尝试自动删除定时任务如下:\n" + cat ${ListJsDrop} + echo + JsDrop=$(cat ${ListJsDrop}) + for Cron in ${JsDrop} + do + perl -i -ne "{print unless / ${Cron}( |$)/}" ${ListCron} + done + crontab ${ListCron} + echo -e "成功删除失效的脚本与定时任务,当前的定时任务清单如下:\n\n--------------------------------------------------------------\n" + crontab -l + echo -e "\n--------------------------------------------------------------\n" + if [ -d ${ScriptsDir}/node_modules ]; then + echo -e "删除失效的定时任务:\n\n${JsDrop}" > ${ContentDropTask} + Notify_DropTask + fi + fi +} + +## 自动增加新的定时任务,需要5个条件:1.AutoAddCron 设置为 true;2.正常更新js脚本,没有报错;3.js-add.list不为空;4.crontab.list存在并且不为空;5.已经正常运行过npm install +## 检测文件:LXK9301/jd_scripts 仓库中的 docker/crontab_list.sh +## 如果检测到检测文件中增加新的定时任务,那么在本地也增加 +## 本功能生效时,会自动从检测文件新增加的任务中读取时间,该时间为北京时间 +function Add_Cron { + if [ "${AutoAddCron}" = "true" ] && [ -s ${ListJsAdd} ] && [ -s ${ListCron} ] && [ -d ${ScriptsDir}/node_modules ]; then + echo -e "开始尝试自动添加定时任务如下:\n" + cat ${ListJsAdd} + echo + JsAdd=$(cat ${ListJsAdd}) + + for Cron in ${JsAdd} + do + if [[ ${Cron} == jd_bean_sign ]] + then + echo "4 0,9 * * * bash ${ShellJd} ${Cron}" >> ${ListCron} + else + cat ${ListCronLxk} | grep -E "\/${Cron}\." | perl -pe "s|(^.+)node */scripts/(j[drx]_\w+)\.js.+|\1bash ${ShellJd} \2|" >> ${ListCron} + fi + done + + if [ $? -eq 0 ] + then + crontab ${ListCron} + echo -e "成功添加新的定时任务,当前的定时任务清单如下:\n\n--------------------------------------------------------------\n" + crontab -l + echo -e "\n--------------------------------------------------------------\n" + if [ -d ${ScriptsDir}/node_modules ]; then + echo -e "成功添加新的定时任务:\n\n${JsAdd}" > ${ContentNewTask} + Notify_NewTask + fi + else + echo -e "添加新的定时任务出错,请手动添加...\n" + if [ -d ${ScriptsDir}/node_modules ]; then + echo -e "尝试自动添加以下新的定时任务出错,请手动添加:\n\n${JsAdd}" > ${ContentNewTask} + Notify_NewTask + fi + fi + fi +} + +## 在日志中记录时间与路径 +echo -e "\n--------------------------------------------------------------\n" +echo -n "系统时间:" +echo $(date "+%Y-%m-%d %H:%M:%S") +if [ "${TZ}" = "UTC" ]; then + echo + echo -n "北京时间:" + echo $(date -d "8 hour" "+%Y-%m-%d %H:%M:%S") +fi +echo -e "\nJS脚本目录:${ScriptsDir}\n" +echo -e "--------------------------------------------------------------\n" + +## 导入配置,更新cron,设置url,更新shell,复制sample,复制entrypoint,发送新配置通知 +Import_Conf "git_pull" +Update_Cron +Reset_RepoUrl +[ -f ${ShellDir}/panel/package.json ] && PanelDependOld=$(cat ${ShellDir}/panel/package.json) +Git_PullShell +[ -f ${ShellDir}/panel/package.json ] && PanelDependNew=$(cat ${ShellDir}/panel/package.json) +Git_PullShellNext + +## 克隆或更新js脚本 +[ -f ${ScriptsDir}/package.json ] && ScriptsDependOld=$(cat ${ScriptsDir}/package.json) +[ -d ${ScriptsDir}/.git ] && Git_PullScripts || Git_CloneScripts +[ -f ${ScriptsDir}/package.json ] && ScriptsDependNew=$(cat ${ScriptsDir}/package.json) + +## 执行各函数 +if [[ ${ExitStatusScripts} -eq 0 ]] +then + echo -e "更新scripts成功...\n" + Diff_Cron + [[ "${ScriptsDependOld}" != "${ScriptsDependNew}" ]] && cd ${ScriptsDir} && Npm_Install scripts + Output_ListJsAdd + Output_ListJsDrop + Del_Cron + Add_Cron +else + echo -e "更新scripts失败,请检查原因...\n" +fi + +## 调用用户自定义的diy.sh +if [[ ${EnableExtraShell} == true ]]; then + if [ -f ${FileDiy} ] + then + . ${FileDiy} + else + echo -e "${FileDiy} 文件不存在,跳过执行DIY脚本...\n" + fi +fi diff --git a/jd.sh b/jd.sh new file mode 100755 index 00000000000..b7143d9c10e --- /dev/null +++ b/jd.sh @@ -0,0 +1,281 @@ +#!/usr/bin/env bash + +## 路径 +ShellDir=${JD_DIR:-$(cd $(dirname $0); pwd)} +[[ ${JD_DIR} ]] && HelpJd=jd || HelpJd=jd.sh +[[ ${JD_DIR} ]] && ShellJd=jd || ShellJd=${ShellDir}/jd.sh +ScriptsDir=${ShellDir}/scripts +ConfigDir=${ShellDir}/config +FileConf=${ConfigDir}/config.sh +FileConfSample=${ShellDir}/sample/config.sh.sample +LogDir=${ShellDir}/log +ListScripts=($(cd ${ScriptsDir}; ls *.js | grep -E "j[drx]_")) +ListCron=${ConfigDir}/crontab.list +ListCronLxk=${ScriptsDir}/docker/crontab_list.sh +ListJs=${LogDir}/js.list + +## 导入config.sh +function Import_Conf { + if [ -f ${FileConf} ] + then + . ${FileConf} + if [ -z "${Cookie1}" ]; then + echo -e "请先在config.sh中配置好Cookie...\n" + exit 1 + fi + else + echo -e "配置文件 ${FileConf} 不存在,请先按教程配置好该文件...\n" + exit 1 + fi +} + +## 更新crontab +function Detect_Cron { + if [[ $(cat ${ListCron}) != $(crontab -l) ]]; then + crontab ${ListCron} + fi +} + +## 用户数量UserSum +function Count_UserSum { + for ((i=1; i<=1000; i++)); do + Tmp=Cookie$i + CookieTmp=${!Tmp} + [[ ${CookieTmp} ]] && UserSum=$i || break + done +} + +## 组合Cookie和互助码子程序 +function Combin_Sub { + CombinAll="" + if [[ ${AutoHelpOther} == true ]] && [[ $1 == ForOther* ]]; then + + ForOtherAll="" + MyName=$(echo $1 | perl -pe "s|ForOther|My|") + + for ((m=1; m<=${UserSum}; m++)); do + TmpA=${MyName}$m + TmpB=${!TmpA} + ForOtherAll="${ForOtherAll}@${TmpB}" + done + + for ((n=1; n<=${UserSum}; n++)); do + for num in ${TempBlockCookie}; do + [[ $n -eq $num ]] && continue 2 + done + CombinAll="${CombinAll}&${ForOtherAll}" + done + + else + for ((i=1; i<=${UserSum}; i++)); do + for num in ${TempBlockCookie}; do + [[ $i -eq $num ]] && continue 2 + done + Tmp1=$1$i + Tmp2=${!Tmp1} + CombinAll="${CombinAll}&${Tmp2}" + done + fi + + echo ${CombinAll} | perl -pe "{s|^&||; s|^@+||; s|&@|&|g; s|@+&|&|g; s|@+|@|g; s|@+$||}" +} + +## 组合Cookie、Token与互助码 +function Combin_All { + export JD_COOKIE=$(Combin_Sub Cookie) + export FRUITSHARECODES=$(Combin_Sub ForOtherFruit) + export PETSHARECODES=$(Combin_Sub ForOtherPet) + export PLANT_BEAN_SHARECODES=$(Combin_Sub ForOtherBean) + export DREAM_FACTORY_SHARE_CODES=$(Combin_Sub ForOtherDreamFactory) + export DDFACTORY_SHARECODES=$(Combin_Sub ForOtherJdFactory) + export JDZZ_SHARECODES=$(Combin_Sub ForOtherJdzz) + export JDJOY_SHARECODES=$(Combin_Sub ForOtherJoy) + export JXNC_SHARECODES=$(Combin_Sub ForOtherJxnc) + export JXNCTOKENS=$(Combin_Sub TokenJxnc) + export BOOKSHOP_SHARECODES=$(Combin_Sub ForOtherBookShop) + export JD_CASH_SHARECODES=$(Combin_Sub ForOtherCash) + export JDSGMH_SHARECODES=$(Combin_Sub ForOtherSgmh) + export JDCFD_SHARECODES=$(Combin_Sub ForOtherCfd) + export JDGLOBAL_SHARECODES=$(Combin_Sub ForOtherGlobal) +} + +## 转换JD_BEAN_SIGN_STOP_NOTIFY或JD_BEAN_SIGN_NOTIFY_SIMPLE +function Trans_JD_BEAN_SIGN_NOTIFY { + case ${NotifyBeanSign} in + 0) + export JD_BEAN_SIGN_STOP_NOTIFY="true" + ;; + 1) + export JD_BEAN_SIGN_NOTIFY_SIMPLE="true" + ;; + esac +} + +## 转换UN_SUBSCRIBES +function Trans_UN_SUBSCRIBES { + export UN_SUBSCRIBES="${goodPageSize}\n${shopPageSize}\n${jdUnsubscribeStopGoods}\n${jdUnsubscribeStopShop}" +} + +## 申明全部变量 +function Set_Env { + Count_UserSum + Combin_All + Trans_JD_BEAN_SIGN_NOTIFY + Trans_UN_SUBSCRIBES +} + +## 随机延迟 +function Random_Delay { + if [[ -n ${RandomDelay} ]] && [[ ${RandomDelay} -gt 0 ]]; then + CurMin=$(date "+%-M") + if [[ ${CurMin} -gt 2 && ${CurMin} -lt 30 ]] || [[ ${CurMin} -gt 31 && ${CurMin} -lt 59 ]]; then + CurDelay=$((${RANDOM} % ${RandomDelay} + 1)) + echo -e "\n命令未添加 \"now\",随机延迟 ${CurDelay} 秒后再执行任务,如需立即终止,请按 CTRL+C...\n" + sleep ${CurDelay} + fi + fi +} + +## 使用说明 +function Help { + echo -e "本脚本的用法为:" + echo -e "1. bash ${HelpJd} xxx # 如果设置了随机延迟并且当时时间不在0-2、30-31、59分内,将随机延迟一定秒数" + echo -e "2. bash ${HelpJd} xxx now # 无论是否设置了随机延迟,均立即运行" + echo -e "3. bash ${HelpJd} runall # 运行所有非挂机脚本,非常耗时" + echo -e "4. bash ${HelpJd} hangup # 重启挂机程序" + echo -e "5. bash ${HelpJd} resetpwd # 重置控制面板用户名和密码" + echo -e "\n针对用法1、用法2中的\"xxx\",可以不输入后缀\".js\",另外,如果前缀是\"jd_\"的话前缀也可以省略。" + echo -e "当前有以下脚本可以运行(仅列出以jd_、jr_、jx_开头的脚本):" + cd ${ScriptsDir} + for ((i=0; i<${#ListScripts[*]}; i++)); do + Name=$(grep "new Env" ${ListScripts[i]} | awk -F "'|\"" '{print $2}') + echo -e "$(($i + 1)).${Name}:${ListScripts[i]}" + done +} + +## nohup +function Run_Nohup { + if [[ $(ps -ef | grep "${js}" | grep -v "grep") != "" ]]; then + ps -ef | grep "${js}" | grep -v "grep" | awk '{print $2}' | xargs kill -9 + fi + [ ! -d ${LogDir}/${js} ] && mkdir -p ${LogDir}/${js} + LogTime=$(date "+%Y-%m-%d-%H-%M-%S") + LogFile="${LogDir}/${js}/${LogTime}.log" + nohup node ${js}.js > ${LogFile} & +} + +## 运行挂机脚本 +function Run_HangUp { + HangUpJs="jd_crazy_joy_coin" + cd ${ScriptsDir} + for js in ${HangUpJs}; do + Import_Conf ${js} && Set_Env + if type pm2 >/dev/null 2>&1; then + pm2 stop ${js}.js 2>/dev/null + pm2 flush + pm2 start -a ${js}.js --watch "${ScriptsDir}/${js}.js" --name="${js}" + else + Run_Nohup >/dev/null 2>&1 + fi + done +} + +## 重置密码 +function Reset_Pwd { + cp -f ${ShellDir}/sample/auth.json ${ConfigDir}/auth.json + echo -e "控制面板重置成功,用户名:admin,密码:adminadmin\n" +} + +## 一次性运行所有脚本 +function Run_All { + if [ ! -f ${ListJs} ]; then + cat ${ListCronLxk} | grep -E "j[drx]_\w+\.js" | perl -pe "s|.+(j[drx]_\w+)\.js.+|\1|" | sort -u > ${ListJs} + fi + echo -e "\n==================== 开始运行所有非挂机脚本 ====================\n" + echo -e "请注意:本过程将非常非常耗时,一个账号可能长达几小时,账号越多耗时越长,如果是手动运行,退出终端也将终止运行。\n" + echo -e "倒计时5秒...\n" + for ((sec=5; sec>0; sec--)); do + echo -e "$sec...\n" + sleep 1 + done + for file in $(cat ${ListJs}); do + echo -e "==================== 运行 $file.js 脚本 ====================\n" + bash ${ShellJd} $file now + done +} + +## 正常运行单个脚本 +function Run_Normal { + Import_Conf $1 && Detect_Cron && Set_Env + + FileNameTmp1=$(echo $1 | perl -pe "s|\.js||") + FileNameTmp2=$(echo $1 | perl -pe "{s|jd_||; s|\.js||; s|^|jd_|}") + SeekDir="${ScriptsDir} ${ScriptsDir}/backUp ${ConfigDir}" + FileName="" + WhichDir="" + + for dir in ${SeekDir} + do + if [ -f ${dir}/${FileNameTmp1}.js ]; then + FileName=${FileNameTmp1} + WhichDir=${dir} + break + elif [ -f ${dir}/${FileNameTmp2}.js ]; then + FileName=${FileNameTmp2} + WhichDir=${dir} + break + fi + done + + if [ -n "${FileName}" ] && [ -n "${WhichDir}" ] + then + [ $# -eq 1 ] && Random_Delay + LogTime=$(date "+%Y-%m-%d-%H-%M-%S") + LogFile="${LogDir}/${FileName}/${LogTime}.log" + [ ! -d ${LogDir}/${FileName} ] && mkdir -p ${LogDir}/${FileName} + cd ${WhichDir} + node ${FileName}.js 2>&1 | tee ${LogFile} + else + echo -e "\n在${ScriptsDir}、${ScriptsDir}/backUp、${ConfigDir}三个目录下均未检测到 $1 脚本的存在,请确认...\n" + Help + fi +} + +## 命令检测 +case $# in + 0) + echo + Help + ;; + 1) + case $1 in + hangup) + Run_HangUp + ;; + resetpwd) + Reset_Pwd + ;; + runall) + Run_All + ;; + *) + Run_Normal $1 + ;; + esac + ;; + 2) + case $2 in + now) + Run_Normal $1 $2 + ;; + *) + echo -e "\n命令输入错误...\n" + Help + ;; + esac + ;; + *) + echo -e "\n命令过多...\n" + Help + ;; +esac diff --git a/package.json b/package.json new file mode 100644 index 00000000000..34d7c93257f --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "private": true, + "scripts": { + "start": "umi dev", + "build": "umi build", + "postinstall": "umi generate tmp", + "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", + "test": "umi-test", + "test:coverage": "umi-test --coverage" + }, + "gitHooks": { + "pre-commit": "lint-staged" + }, + "lint-staged": { + "*.{js,jsx,less,md,json}": [ + "prettier --write" + ], + "*.ts?(x)": [ + "prettier --parser=typescript --write" + ] + }, + "dependencies": { + "@ant-design/pro-layout": "^6.5.0", + "@umijs/preset-react": "1.x", + "codemirror": "^5.59.4", + "qrcode.react": "^1.0.1", + "react-codemirror2": "^7.2.1", + "react-diff-viewer": "^3.1.1", + "umi": "^3.3.9", + "umi-request": "^1.3.5" + }, + "devDependencies": { + "@types/qrcode.react": "^1.0.1", + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "@umijs/test": "^3.3.9", + "lint-staged": "^10.0.7", + "prettier": "^2.2.0", + "react": "17.x", + "react-dom": "17.x", + "typescript": "^4.1.2", + "yorkie": "^2.0.0" + } +} diff --git a/rm_log.sh b/rm_log.sh new file mode 100755 index 00000000000..565c2902eca --- /dev/null +++ b/rm_log.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +## 判断环境 +ShellDir=${JD_DIR:-$(cd $(dirname $0); pwd)} +LogDir=${ShellDir}/log + +## 导入配置文件 +. ${ShellDir}/config/config.sh + +## 删除运行js脚本的旧日志 +function Rm_JsLog { + LogFileList=$(ls -l ${LogDir}/*/*.log | awk '{print $9}') + for log in ${LogFileList} + do + LogDate=$(echo ${log} | awk -F "/" '{print $NF}' | cut -c1-10) #文件名比文件属性获得的日期要可靠 + if [[ $(uname -s) == Darwin ]] + then + DiffTime=$(($(date +%s) - $(date -j -f "%Y-%m-%d" "${LogDate}" +%s))) + else + DiffTime=$(($(date +%s) - $(date +%s -d "${LogDate}"))) + fi + [ ${DiffTime} -gt $((${RmLogDaysAgo} * 86400)) ] && rm -vf ${log} + done +} + +## 删除git_pull.sh的运行日志 +function Rm_GitPullLog { + if [[ $(uname -s) == Darwin ]] + then + DateDelLog=$(date -v-${RmLogDaysAgo}d "+%Y-%m-%d") + else + Stmp=$(($(date "+%s") - 86400 * ${RmLogDaysAgo})) + DateDelLog=$(date -d "@${Stmp}" "+%Y-%m-%d") + fi + LineEndGitPull=$[$(cat ${LogDir}/git_pull.log | grep -n "${DateDelLog} " | head -1 | awk -F ":" '{print $1}') - 3] + [ ${LineEndGitPull} -gt 0 ] && perl -i -ne "{print unless 1 .. ${LineEndGitPull} }" ${LogDir}/git_pull.log +} + +## 删除空文件夹 +function Rm_EmptyDir { + cd ${LogDir} + for dir in $(ls) + do + if [ -d ${dir} ] && [[ $(ls ${dir}) == "" ]]; then + rm -rf ${dir} + fi + done +} + +## 运行 +if [ -n "${RmLogDaysAgo}" ]; then + echo -e "查找旧日志文件中...\n" + Rm_JsLog + Rm_GitPullLog + Rm_EmptyDir + echo -e "删除旧日志执行完毕\n" +fi diff --git a/sample/auth.json b/sample/auth.json new file mode 100755 index 00000000000..65540d07bc7 --- /dev/null +++ b/sample/auth.json @@ -0,0 +1 @@ +{"user":"admin","password":"adminadmin"} \ No newline at end of file diff --git a/sample/config.sh.sample b/sample/config.sh.sample new file mode 100755 index 00000000000..e0069c1095a --- /dev/null +++ b/sample/config.sh.sample @@ -0,0 +1,636 @@ +## Version: v3.32.1 +## Date: 2021-03-14 +## Update Content: 增加三个通知控制变量DDQ_NOTIFY_CONTROL、CASH_NOTIFY_CONTROL和JDZZ_NOTIFY_CONTROL。 + +## 上面版本号中,如果第2位数字有变化,那么代表增加了新的参数,如果只有第3位数字有变化,仅代表更新了注释,没有增加新的参数,可更新可不更新 + +################################## 说明 ################################## +## 以下配置中,带有 export 申明的,均由lxk0301大佬定义,其他互助码,考虑到直接按lxk0301大佬定义的变量去填的话,一是不方便记忆,二是容易搞混,所以最终的变量将由脚本去组合,你只要按注释去填即可 +## 除此之外,还额外增加了是否自动删除失效任务AutoDelCron、是否自动增加新任务AutoAddCron、删除旧日志时间RmLogDaysAgo、随机延迟启动任务RandomDelay、是否添加自定义脚本EnableExtraShell五个人性化的设置供选择 +## 所有赋值等号两边不能有空格,所有的值请一律在两侧添加半角的双引号,如果变量值中含有双引号,则外侧改为一对半角的单引号。 +## 所有的赋值都可以参考 “定义jd_pet是否静默运行” 部分,在不同时间设置不同的值,以达到你想要的效果,具体判断条件如下: +## $(date "+%-d") 当前的日期,如:13 +## $(date "+%-w") 当前是星期几,如:3 +## $(date "+%-H") 当前的小时数,如:23 +## $(date "+%-M") 当前的分钟数,如:49 +## 其他date命令的更多用法,可以在命令行中输入 date --help 查看 +## 判断条件 -eq -ne -gt -ge -lt -le ,具体含义可百度一下 + + +################################## 定义Cookie(必填) ################################## +## 请依次填入每个用户的Cookie,Cookie的具体形式(只有pt_key字段和pt_pin字段,没有其他字段):pt_key=xxxxxxxxxx;pt_pin=xxxx; +## 1. 如果是通过控制面板编辑本文件,点击页面上方“扫码获取Cookie”即可获取,此方式获取的Cookie有效期为3个月 +## 2. 还可以通过浏览器开发工具获取,此方式获得的Cookie只有1个月有效期 +## 必须按数字顺序1、2、3、4...依次编号下去,例子只有6个,超出6个你继续往下编号即可 +## 不允许有汉字,如果ID有汉字,请在PC浏览器上获取Cookie,会自动将汉字转换为URL编码 +Cookie1="" +Cookie2="" +Cookie3="" +Cookie4="" +Cookie5="" +Cookie6="" + + +################################## 临时屏蔽某个Cookie(选填) ################################## +## 如果某些Cookie已经失效了,但暂时还没法更新,可以使用此功能在不删除该Cookie和重新修改Cookie编号的前提下,临时屏蔽掉某些编号的Cookie +## 多个Cookie编号以半角的空格分隔,两侧一对半角双引号,使用此功能后,在运行js脚本时账号编号将发生变化 +## 举例1:TempBlockCookie="2" 临时屏蔽掉Cookie2 +## 举例2:TempBlockCookie="2 4" 临时屏蔽掉Cookie2和Cookie4 +## 如果只是想要屏蔽某个账号不玩某些小游戏,可以参考下面 case 这个命令的例子来控制,脚本名称请去掉后缀 “.js” +## case $1 in +## jd_fruit) +## TempBlockCookie="5" # 账号5不玩jd_fruit +## ;; +## jd_dreamFactory | jd_jdfactory) +## TempBlockCookie="2" # 账号2不玩jd_dreamFactory和jd_jdfactory +## ;; +## jd_jdzz | jd_joy) +## TempBlockCookie="3 6" # 账号3、账号6不玩jd_jdzz和jd_joy +## ;; +## esac +TempBlockCookie="" + + +################################## 定义是否自动删除失效的脚本与定时任务(选填) ################################## +## 有的时候,某些JS脚本只在特定的时间有效,过了时间就失效了,需要自动删除失效的本地定时任务,则设置为 "true" ,否则请设置为 "false" +## 检测文件:lxk0301/jd_scripts 仓库中的 docker/crontab_list.sh +## 当设置为 "true" 时,会自动从检测文件中读取比对删除的任务(识别以“jd_”、“jr_”、“jx_”开头的任务) +## 当设置为 "true" 时,脚本只会删除一整行失效的定时任务,不会修改其他现有任务,所以任何时候,你都可以自己调整你的crontab.list +## 当设置为 "true" 时,如果你有添加额外脚本是以“jd_”“jr_”“jx_”开头的,如检测文件中,会被删除,不是以“jd_”“jr_”“jx_”开头的任务则不受影响 +AutoDelCron="true" + + +################################## 定义是否自动增加新的本地定时任务(选填) ################################## +## lxk0301 大佬会在有需要的时候,增加定时任务,如需要本地自动增加新的定时任务,则设置为 "true" ,否则请设置为 "false" +## 检测文件:lxk0301/jd_scripts 仓库中的 docker/crontab_list.sh +## 当设置为 "true" 时,如果检测到检测文件中有增加新的定时任务,那么在本地也增加(识别以“jd_”、“jr_”、“jx_”开头的任务) +## 当设置为 "true" 时,会自动从检测文件新增加的任务中读取时间,该时间为北京时间 +## 当设置为 "true" 时,脚本只会增加新的定时任务,不会修改其他现有任务,所以任何时候,你都可以自己调整你的crontab.list +AutoAddCron="true" + + +################################## 定义删除日志的时间(选填) ################################## +## 定义在运行删除旧的日志任务时,要删除多少天以前的日志,请输入正整数,不填则禁用删除日志的功能 +RmLogDaysAgo="7" + + +################################## 定义随机延迟启动任务(选填) ################################## +## 如果任务不是必须准点运行的任务,那么给它增加一个随机延迟,由你定义最大延迟时间,单位为秒,如 RandomDelay="300" ,表示任务将在 1-300 秒内随机延迟一个秒数,然后再运行 +## 在crontab.list中,在每小时第0-2分、第30-31分、第59分这几个时间内启动的任务,均算作必须准点运行的任务,在启动这些任务时,即使你定义了RandomDelay,也将准点运行,不启用随机延迟 +## 在crontab.list中,除掉每小时上述时间启动的任务外,其他任务在你定义了 RandomDelay 的情况下,一律启用随机延迟,但如果你按照Wiki教程给某些任务添加了 "now",那么这些任务也将无视随机延迟直接启动 +RandomDelay="300" + + +################################## 定义User-Agent(选填) ################################## +## 自定义lxk0301大佬仓库里JD系列js脚本的User-Agent,不懂不知不会User-Agent的请不要随意填写内容,随意填写了出错概不负责 +## 如需使用,请自行解除下一行注释 +# export JD_USER_AGENT="" + + +################################## 定义通知TOKEN(选填) ################################## +## 想通过什么渠道收取通知,就填入对应渠道的值 +## 1. ServerChan,教程:http://sc.ftqq.com/3.version +export PUSH_KEY="" + +## 2. BARK,教程(看BARK_PUSH和BARK_SOUND的说明):https://gitee.com/lxk0301/jd_docker/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 +export BARK_PUSH="" +export BARK_SOUND="" + +## 3. Telegram,如需使用,TG_BOT_TOKEN和TG_USER_ID必须同时赋值,教程:https://gitee.com/lxk0301/jd_docker/blob/master/backUp/TG_PUSH.md +export TG_BOT_TOKEN="" +export TG_USER_ID="" + +## 4. 钉钉,教程(看DD_BOT_TOKEN和DD_BOT_SECRET部分):https://gitee.com/lxk0301/jd_docker/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 +export DD_BOT_TOKEN="" +export DD_BOT_SECRET="" + +## 5. iGot聚合推送,支持多方式推送,填写iGot的推送key。教程:https://wahao.github.io/Bark-MP-helper/#/ +export IGOT_PUSH_KEY="" + +## 6. Push Plus,微信扫码登录后一对一推送或一对多推送,参考文档:http://pushplus.hxtrip.com/ +## 其中PUSH_PLUS_USER是一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码)注:(1、需订阅者扫描二维码 2、如果您是创建群组所属人,也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送),只填PUSH_PLUS_TOKEN默认为一对一推送 +export PUSH_PLUS_TOKEN="" +export PUSH_PLUS_USER="" + +## 7. 企业微信机器人消息推送 webhook 后面的 key,文档:https://work.weixin.qq.com/api/doc/90000/90136/91770 +export QYWX_KEY="" + +## 8. 企业微信应用消息推送的值,文档:https://work.weixin.qq.com/api/doc/90000/90135/90236 +## 依次填上corpid的值,corpsecret的值,touser的值,agentid,media_id的值,注意用,号隔开,例如:"wwcff56746d9adwers,B-791548lnzXBE6_BWfxdf3kSTMJr9vFEPKAbh6WERQ,mingcheng,1000001,2COXgjH2UIfERF2zxrtUOKgQ9XklUqMdGSWLBoW_lSDAdafat" +export QYWX_AM="" + + +################################## Telegram 代理(选填) ################################## +## Telegram 代理的 IP,代理类型为 http,比如你代理是 http://127.0.0.1:1080,则填写 export TG_PROXY_HOST="127.0.0.1",export TG_PROXY_PORT="1080" +## 如需使用,请自行解除注释 +# export TG_PROXY_HOST="" +# export TG_PROXY_PORT="" + +## Telegram api自建的反向代理地址,例子:反向代理地址 http://aaa.bbb.ccc 则填写 aaa.bbb.ccc,教程:https://www.hostloc.com/thread-805441-1-1.html +## 如需使用,请自行解除下注释 +# export TG_API_HOST="" + + +################################## 定义每日签到的通知形式(选填) ################################## +## js脚本每日签到提供3种通知方式,分别为: +## 关闭通知,那么请在下方填入0 +## 简洁通知,那么请在下方填入1,其效果见:https://gitee.com/lxk0301/jd_docker/blob/master/icon/bean_sign_simple.jpg +## 原始通知,那么请在下方填入2,如果不填也默认为2,内容比较长,这也是默认通知方式 +NotifyBeanSign="" + + +################################## 定义每日签到每个接口间的延迟时间(选填) ################################## +## 默认每个签到接口并发无延迟,如需要依次进行每个接口,请自定义延迟时间,单位为毫秒,延迟作用于每个签到接口, 如填入延迟则切换顺序签到(耗时较长) +## 例: "2000" 则表示每个接口延迟2秒; "2000-5000" 则表示每个接口之间最小2秒至最大5秒内的随机延迟 +export JD_BEAN_STOP="" + + +################################## 自动按顺序进行账号间互助(选填) ################################## +## 设置为 true 时,以下所有互助活动,账号间将按照config.sh中Cookie顺序进行互助,此时,不会助力不在config.sh中的账号,无法和别人交换助力 +## MyXxxx系列变量仍然需要填写,但ForOtherXxxx系列变量不再需要填写(填写了也无效) +## 如果启用了TempBlockCookie,那么只是被屏蔽的账号不助力其他账号,其他账号还是会助力被屏蔽的账号 +AutoHelpOther="" + + +################################## 定义导出互助码模板样式(选填) ################################## +## 定义 export_sharecodes.sh 导出的互助码模板样式,目前预定义三种模板,其他模板待开发。 +## 不填则默认按“普通优先级助力模板”导出,Cookie编号在前的优先助力 +## 填 0 将使用“统一优先级助力模板”,所有账户要助力的码全部一致,和启用 AutoHelpOther 的效果差不多 +## 填 1 使用“均匀助力模板”,所有账户获得助力次数一致 +HelpType="" + + +################################## 定义jd_fruit互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyFruit1="" +MyFruit2="" +MyFruit3="" +MyFruit4="" +MyFruit5="" +MyFruit6="" +MyFruitA="" +MyFruitB="" + +ForOtherFruit1="" +ForOtherFruit2="" +ForOtherFruit3="" +ForOtherFruit4="" +ForOtherFruit5="" +ForOtherFruit6="" + + +################################## 定义jd_pet互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyPet1="" +MyPet2="" +MyPet3="" +MyPet4="" +MyPet5="" +MyPet6="" +MyPetA="" +MyPetB="" + +ForOtherPet1="" +ForOtherPet2="" +ForOtherPet3="" +ForOtherPet4="" +ForOtherPet5="" +ForOtherPet6="" + + +################################## 定义jd_plantBean互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyBean1="" +MyBean2="" +MyBean3="" +MyBean4="" +MyBean5="" +MyBean6="" +MyBeanA="" +MyBeanB="" + +ForOtherBean1="" +ForOtherBean2="" +ForOtherBean3="" +ForOtherBean4="" +ForOtherBean5="" +ForOtherBean6="" + + +################################## 定义jd_dreamFactory互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyDreamFactory1="" +MyDreamFactory2="" +MyDreamFactory3="" +MyDreamFactory4="" +MyDreamFactory5="" +MyDreamFactory6="" +MyDreamFactoryA="" +MyDreamFactoryB="" + +ForOtherDreamFactory1="" +ForOtherDreamFactory2="" +ForOtherDreamFactory3="" +ForOtherDreamFactory4="" +ForOtherDreamFactory5="" +ForOtherDreamFactory6="" + + +################################## 定义jd_jdfactory互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyJdFactory1="" +MyJdFactory2="" +MyJdFactory3="" +MyJdFactory4="" +MyJdFactory5="" +MyJdFactory6="" +MyJdFactoryA="" +MyJdFactoryB="" + +ForOtherJdFactory1="" +ForOtherJdFactory2="" +ForOtherJdFactory3="" +ForOtherJdFactory4="" +ForOtherJdFactory5="" +ForOtherJdFactory6="" + + +################################## 定义jd_jdzz互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyJdzz1="" +MyJdzz2="" +MyJdzz3="" +MyJdzz4="" +MyJdzz5="" +MyJdzz6="" +MyJdzzA="" +MyJdzzB="" + +ForOtherJdzz1="" +ForOtherJdzz2="" +ForOtherJdzz3="" +ForOtherJdzz4="" +ForOtherJdzz5="" +ForOtherJdzz6="" + + +################################## 定义jd_crazy_joy互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyJoy1="" +MyJoy2="" +MyJoy3="" +MyJoy4="" +MyJoy5="" +MyJoy6="" +MyJoyA="" +MyJoyB="" + +ForOtherJoy1="" +ForOtherJoy2="" +ForOtherJoy3="" +ForOtherJoy4="" +ForOtherJoy5="" +ForOtherJoy6="" + + +################################## 定义jd_jxnc互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +## jd_jxnc助力码为 JSON 格式因此使用单引号,json 格式如下 +## {"smp":"22bdadsfaadsfadse8a","active":"jdnc_1_btorange210113_2","joinnum":"1"} +## 助力码获取可以通过 bash jd.sh jd_get_share_code now 命令获取 +## 注意:jd_jxnc 种植种子发生变化的时候,互助码也会变!! +MyJxnc1='' +MyJxnc2='' +MyJxnc3='' +MyJxnc4='' +MyJxnc5='' +MyJxnc6='' +MyJxncA='' +MyJxncB='' + +ForOtherJxnc1="" +ForOtherJxnc2="" +ForOtherJxnc3="" +ForOtherJxnc4="" +ForOtherJxnc5="" +ForOtherJxnc6="" + + +################################## 定义jd_bookshop互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyBookShop1="" +MyBookShop2="" +MyBookShop3="" +MyBookShop4="" +MyBookShop5="" +MyBookShop6="" +MyBookShopA="" +MyBookShopB="" + +ForOtherBookShop1="" +ForOtherBookShop2="" +ForOtherBookShop3="" +ForOtherBookShop4="" +ForOtherBookShop5="" +ForOtherBookShop6="" + + +################################## 定义jd_cash互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyCash1="" +MyCash2="" +MyCash3="" +MyCash4="" +MyCash5="" +MyCash6="" +MyCashA="" +MyCashB="" + +ForOtherCash1="" +ForOtherCash2="" +ForOtherCash3="" +ForOtherCash4="" +ForOtherCash5="" +ForOtherCash6="" + + +################################## 定义jd_sgmh互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MySgmh1="" +MySgmh2="" +MySgmh3="" +MySgmh4="" +MySgmh5="" +MySgmh6="" +MySgmhA="" +MySgmhB="" + +ForOtherSgmh1="" +ForOtherSgmh2="" +ForOtherSgmh3="" +ForOtherSgmh4="" +ForOtherSgmh5="" +ForOtherSgmh6="" + + +################################## 定义jd_cfd活动互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyCfd1="" +MyCfd2="" +MyCfd3="" +MyCfd4="" +MyCfd5="" +MyCfd6="" +MyCfdA="" +MyCfdB="" + +ForOtherCfd1="" +ForOtherCfd2="" +ForOtherCfd3="" +ForOtherCfd4="" +ForOtherCfd5="" +ForOtherCfd6="" + + +################################## 定义jd_global活动互助(选填) ################################## +## 具体填法及要求详见本文件最下方“互助码填法示例” +MyGlobal1="" +MyGlobal2="" +MyGlobal3="" +MyGlobal4="" +MyGlobal5="" +MyGlobal6="" +MyGlobalA="" +MyGlobalB="" + +ForOtherGlobal1="" +ForOtherGlobal2="" +ForOtherGlobal3="" +ForOtherGlobal4="" +ForOtherGlobal5="" +ForOtherGlobal6="" + + +################################## 定义jd_superMarket蓝币兑换数量(选填) ################################## +## jd_superMarket蓝币兑换,可用值包括: +## 一、0:表示不兑换京豆,这也是js脚本的默认值 +## 二、20:表示兑换20个京豆 +## 三、1000:表示兑换1000个京豆 +## 四、可兑换清单的商品名称,输入能跟唯一识别出来的关键词即可,比如:MARKET_COIN_TO_BEANS="抽纸" +## 注意:有些比较贵的实物商品JD只是展示出来忽悠人的,即使你零点用脚本去抢,也会提示没有或提示已下架 +export MARKET_COIN_TO_BEANS="0" + + +################################## 定义jd_superMarket蓝币成功兑换奖品是否静默运行(选填) ################################## +## 默认 "false" 关闭(即:奖品兑换成功后会发出通知提示),如需要静默运行不发出通知,请改为 "true" +export MARKET_REWARD_NOTIFY="" + + +################################## 定义jd_superMarket是否自动使用金币去抽奖(选填) ################################## +## 是否用金币去抽奖,默认 "false" 关闭,如需开启,请修改为 "true" +export SUPERMARKET_LOTTERY="" + + +################################## 定义jd_superMarket是否自动参加PK队伍(选填) ################################## +## 是否每次PK活动参加脚本作者创建的PK队伍,"true" 表示参加,"false" 表示不参加,默认为 "true" +export JOIN_PK_TEAM="" + + +################################## 定义jd_fruit是否静默运行(选填) ################################## +## 默认为 "false",不静默,发送推送通知消息,如不想收到通知,请修改为 "true" +## 如果你不想完全关闭或者完全开启通知,只想在特定的时间发送通知,可以参考下面的“定义jd_pet是否静默运行”部分,设定几个if判断条件 +export FRUIT_NOTIFY_CONTROL="" + + +################################## 定义jd_fruit是否使用水滴换豆卡(选填) ################################## +## 如果出现限时活动时100g水换20豆,此时比浇水划算,"true" 表示换豆(不浇水),"false" 表示不换豆(继续浇水),默认是"false" +## 如需切换为换豆(不浇水),请修改为 "true" +export FRUIT_BEAN_CARD="" + + +################################## 定义jd_joy喂食克数(选填) ################################## +## 你期望的jd_joy每次喂食克数,只能填入10、20、40、80,默认为10 +## 如实际持有食物量小于所设置的克数,脚本会自动降一档,直到降无可降 +## 具体情况请自行在jd_joy游戏中去查阅攻略 +export JOY_FEED_COUNT="" + + +################################## 定义jd_joy兑换京豆数量(选填) ################################## +## 目前的可用值包括:0、20、500、1000,其中0表示为不自动兑换京豆,如不设置,将默认为"20" +## 不同等级可兑换不同数量的京豆,详情请见jd_joy游戏中兑换京豆选项 +## 500、1000的京豆每天有总量限制,设置了并且你也有足够积分时,也并不代表就一定能抢到 +export JD_JOY_REWARD_NAME="" + + +################################## 定义jd_joy兑换京豆是否静默运行(选填) ################################## +## 默认为 "false",在成功兑换京豆时将发送推送通知消息(失败不发送),如想要静默不发送通知,请修改为 "true" +export JD_JOY_REWARD_NOTIFY="" + + +################################## 定义jd_joy是否自动给好友的汪汪喂食(选填) ################################## +## 默认 "false" 不会自动给好友的汪汪喂食,如想自动喂食,请改成 "true" +export JOY_HELP_FEED="" + + +################################## 定义jd_joy是否自动报名宠物赛跑(选填) ################################## +## 默认 "true" 参加宠物赛跑,如需关闭,请改成 "false" +export JOY_RUN_FLAG="" + + +################################## 定义jd_joy参加比赛类型(选填) ################################## +## 当JOY_RUN_FLAG不设置或设置为 "true" 时生效 +## 可选值:2,10,50,其他值不可以。其中2代表参加双人PK赛,10代表参加10人突围赛,50代表参加50人挑战赛,不填时默认为2 +## 各个账号间请使用 & 分隔,比如:JOY_TEAM_LEVEL="2&2&50&10" +## 如果你有5个账号但只写了四个数字,那么第5个账号将默认参加2人赛,账号如果更多,与此类似 +export JOY_TEAM_LEVEL="" + + +################################## 定义jd_joy赛跑自己账号内部是否开启互助(选填) ################################## +## 输入 true 为开启内部互助 +export JOY_RUN_HELP_MYSELF="" + + +################################## 定义jd_joy赛跑获胜后是否推送通知(选填) ################################## +## 控制jd_joy.js脚本jd_joy赛跑获胜后是否推送通知,"false" 为否(不推送通知消息),"true" 为是(即:发送推送通知消息),默认为 "true" +export JOY_RUN_NOTIFY="" + + +################################## 定义jd_moneyTree是否自动将金果卖出变成金币(选填) ################################## +## 金币有时效,默认为 "false",不卖出金果为金币,如想希望自动卖出,请修改为 "true" +export MONEY_TREE_SELL_FRUIT="" + + +################################## 定义jd_pet是否静默运行(选填) ################################## +## 默认 "false"(不静默,发送推送通知消息),如想静默请修改为 true +## 每次执行脚本通知太频繁了,改成只在周三和周六中午那一次运行时发送通知提醒 +## 除掉上述提及时间之外,均设置为 true,静默不发通知 +## 特别说明:针对北京时间有效。 +if [ $(date "+%-w") -eq 6 ] && [ $(date "+%-H") -ge 9 ] && [ $(date "+%-H") -lt 14 ]; then + export PET_NOTIFY_CONTROL="false" +elif [ $(date "+%-w") -eq 3 ] && [ $(date "+%-H") -ge 9 ] && [ $(date "+%-H") -lt 14 ]; then + export PET_NOTIFY_CONTROL="false" +else + export PET_NOTIFY_CONTROL="true" +fi + + +################################## 定义jd_dreamFactory控制哪个JD账号不运行此脚本(选填) ################################## +## 输入"1"代表第一个JD账号不运行,多个使用 & 连接,例:"1&3" 代表账号1和账号3不运行jd_dreamFactory脚本,注:输入"0",代表全部账号不运行jd_dreamFactory脚本 +## 如果使用了 “临时屏蔽某个Cookie” TempBlockCookie 功能,编号会发生变化 +export DREAMFACTORY_FORBID_ACCOUNT="" + + +################################## 定义jd_jdfactory控制哪个JD账号不运行此脚本(选填) ################################## +## 输入"1"代表第一个JD账号不运行,多个使用 & 连接,例:"1&3" 代表账号1和账号3不运行jd_jdfactory脚本,注:输入"0",代表全部账号不运行jd_jdfactory脚本 +## 如果使用了 “临时屏蔽某个Cookie” TempBlockCookie 功能,编号会发生变化 +export JDFACTORY_FORBID_ACCOUNT="" + + +################################## 定义jd_jdfactory心仪的商品(选填) ################################## +## 只有在满足以下条件时,才自动投入电力:一是存储的电力满足生产商品所需的电力,二是心仪的商品有库存,如果没有输入心仪的商品,那么当前你正在生产的商品视作心仪的商品 +## 如果你看不懂上面的话,请去jd_jdfactory游戏中查阅攻略 +## 心仪的商品请输入商品的全称或能唯一识别出该商品的关键字 +export FACTORAY_WANTPRODUCT_NAME="" + + +################################## 定义jd_jxnc通知级别(选填) ################################## +## 可用值: 0(不通知); 1(本次获得水滴>0); 2(任务执行); 3(任务执行+未种植种子),默认为"3" +export JXNC_NOTIFY_LEVEL="3" + + +################################## 定义jd_cfd通知开关(选填) ################################## +## 输入 true 为通知,不填则为不通知 +export CFD_NOTIFY_CONTROL="" + + +################################## 定义jd_jxd是否自动把抽奖卷兑换为兑币 ################################## +## 输入 true 为自动兑换,不填则为不兑换 +export JD_JXD_EXCHANGE="" + + +################################## 定义jd_necklace是否是否静默运行 ################################## +## 控制点点券是否静默运行,false 为否(默认值false,发送推送通知消息),true 为是(即:不发送推送通知消息) +export DDQ_NOTIFY_CONTROL="" + + +################################## 定义jd_cash是否是否静默运行 ################################## +## 控制签到领现金是否静默运行,false 为否(默认值false,发送推送通知消息),true 为是(即:不发送推送通知消息) +export CASH_NOTIFY_CONTROL="" + + +################################## 定义jd_jdzz是否是否静默运行 ################################## +## 控制京东赚赚是否静默运行,false 为否(默认值false,发送推送通知消息),默认每月1日推送一次通知,true 为是(即:不发送推送通知消息), +export JDZZ_NOTIFY_CONTROL="" + + +################################## 定义取关参数(选填) ################################## +## jd_unsubscribe这个任务是用来取关每天做任务关注的商品和店铺,默认在每次运行时取关20个商品和20个店铺 +## 如果取关数量不够,可以根据情况增加,还可以设置 jdUnsubscribeStopGoods 和 jdUnsubscribeStopShop +## 商品取关数量 +goodPageSize="" +## 店铺取关数量 +shopPageSize="" +## 遇到此商品不再取关此商品以及它后面的商品,需去商品详情页长按拷贝商品信息 +jdUnsubscribeStopGoods="" +## 遇到此店铺不再取关此店铺以及它后面的店铺,请从头开始输入店铺名称 +jdUnsubscribeStopShop="" + + +################################## 定义注销店铺会员参数(选填) ################################## +## jd_unbind脚本需要的,注销JD已开的店铺会员,不是注销JDplus会员,个别店铺无法注销 +## 此参数控制每次运行脚本时注销多少个店铺会员,默认200 +export UN_BIND_CARD_NUM="" + +## 遇到此参数设定的会员卡则跳过不进行注销,多个会员卡之间以 & 分隔,默认值"JDPLUS会员" +export UN_BIND_STOP_CARD="" + + +################################## jd_crazy_joy(选填) ################################## +## jd_crazy_joy循环助力,"true" 表示循环助力,"false" 表示不循环助力,默认 "false" +export JDJOY_HELPSELF="" + +## jd_crazy_joy京豆兑换,目前最小值为500/1000京豆,默认为 "0" 不开启京豆兑换 +export JDJOY_APPLYJDBEAN="" + +## jd_crazy_joy自动购买什么等级的JOY,如需要使用请自行解除注释 +# export BUY_JOY_LEVEL="" + + +################################## 定义是否自动加购物车(选填) ################################## +## jd_bookshop和jd_small_home有些任务需要将商品加进购物车才能完成,默认 "false" 不做这些任务,如想做,请设置为 "true" +export PURCHASE_SHOPS="" + + +################################## 是否添加DIY脚本(选填) ################################## +## 如果你自己会写shell脚本,并且希望在每次git_pull.sh这个脚本运行时,额外运行你的DIY脚本,请赋值为 "true" +## 同时,请务必将你的脚本命名为 diy.sh (只能叫这个文件名),放在 config 目录下 +## 我已定义好的变量,你如果想直接使用,可以参考本仓库下 git_pull.sh 文件 +EnableExtraShell="" + + +################################## 互助码填法示例 ################################## +## **互助码是填在My系列变量中的,ForOther系统变量中只要填入My系列的变量名即可,按注释中的例子拼接,以jd_fruit为例,如下所示。** +## **实际上jd_fruit一个账号只能给别人助力3次,我多写的话,只有前几个会被助力。但如果前面的账号获得的助力次数已经达到上限了,那么还是会尝试继续给余下的账号助力,所以多填也是有意义的。** +## **ForOther系列变量必须从1开始编号,依次编下去。** + +# MyFruit1="e6e04602d5e343258873af1651b603ec" # 这是Cookie1这个账号的互助码 +# MyFruit2="52801b06ce2a462f95e1d59d7e856ef4" # 这是Cookie2这个账号的互助码 +# MyFruit3="e2fd1311229146cc9507528d0b054da8" # 这是Cookie3这个账号的互助码 +# MyFruit4="6dc9461f662d490991a31b798f624128" # 这是Cookie4这个账号的互助码 +# MyFruit5="30f29addd75d44e88fb452bbfe9f2110" # 这是Cookie5这个账号的互助码 +# MyFruit6="1d02fc9e0e574b4fa928e84cb1c5e70b" # 这是Cookie6这个账号的互助码 +# MyFruitA="5bc73a365ff74a559bdee785ea97fcc5" # 这是我和别人交换互助,另外一个用户A的互助码 +# MyFruitB="6d402dcfae1043fba7b519e0d6579a6f" # 这是我和别人交换互助,另外一个用户B的互助码 +# MyFruitC="5efc7fdbb8e0436f8694c4c393359576" # 这是我和别人交换互助,另外一个用户C的互助码 + +# ForOtherFruit1="${MyFruit2}@${MyFruitB}@${MyFruit4}" # Cookie1这个账号助力Cookie2的账号的账号、Cookie4的账号以及用户B +# ForOtherFruit2="${MyFruit1}@${MyFruitA}@${MyFruit4}" # Cookie2这个账号助力Cookie1的账号的账号、Cookie4的账号以及用户A +# ForOtherFruit3="${MyFruit1}@${MyFruit2}@${MyFruitC}@${MyFruit4}@${MyFruitA}@${MyFruit6}" # 解释同上,jd_fruit实际上只能助力3次 +# ForOtherFruit4="${MyFruit1}@${MyFruit2}@${MyFruit3}@${MyFruitC}@${MyFruit6}@${MyFruitA}" # 解释同上,jd_fruit实际上只能助力3次 +# ForOtherFruit5="${MyFruit1}@${MyFruit2}@${MyFruit3}@${MyFruitB}@${MyFruit4}@${MyFruit6}@${MyFruitC}@${MyFruitA}" +# ForOtherFruit6="${MyFruit1}@${MyFruit2}@${MyFruit3}@${MyFruitA}@${MyFruit4}@${MyFruit5}@${MyFruitC}" + + +################################## 额外的环境变量(选填) ################################## +## 请在以下补充你需要用到的额外的环境变量,形式:export 变量名="变量值" + diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..096c16cb5a0a848fbc454b6072f33673fac6e348 GIT binary patch literal 15995 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 literal 0 HcmV?d00001 diff --git a/src/layouts/defaultProps.tsx b/src/layouts/defaultProps.tsx new file mode 100644 index 00000000000..33fd285f54d --- /dev/null +++ b/src/layouts/defaultProps.tsx @@ -0,0 +1,81 @@ +import { + FormOutlined, + FieldTimeOutlined, + DiffOutlined, + SettingOutlined, + CodeOutlined, + FolderOutlined, + LockOutlined, + RadiusSettingOutlined, +} from '@ant-design/icons'; +import logo from '@/assets/logo.png'; + +export default { + route: { + routes: [ + { + name: 'login', + path: '/login', + hideInMenu: true, + component: '@/pages/login/index', + }, + { + path: '/cookie', + name: 'Cookie管理', + icon: , + component: '@/pages/cookie/index', + }, + { + path: '/config', + name: '配置文件', + icon: , + component: '@/pages/config/index', + }, + { + path: '/diy', + name: '自定义脚本', + icon: , + component: '@/pages/diy/index', + }, + { + path: '/crontab', + name: '定时任务', + icon: , + component: '@/pages/crontab/index', + }, + { + path: '/diff', + name: '对比工具', + icon: , + component: '@/pages/diff/index', + }, + { + path: '/code', + name: '互助码', + icon: , + component: '@/pages/code/index', + }, + { + path: '/log', + name: '日志', + icon: , + component: '@/pages/log/index', + }, + { + path: '/password', + name: '修改密码', + icon: , + component: '@/pages/password/index', + }, + ], + }, + location: { + pathname: '/', + }, + fixSiderbar: true, + navTheme: 'light', + primaryColor: '#1890ff', + contentWidth: 'Fixed', + splitMenus: false, + logo: logo, +} as any; diff --git a/src/layouts/index.less b/src/layouts/index.less new file mode 100644 index 00000000000..77d80496236 --- /dev/null +++ b/src/layouts/index.less @@ -0,0 +1,23 @@ +body { + height: 100%; + overflow-y: hidden; + background-color: rgb(248, 248, 248); +} + +@import '~codemirror/lib/codemirror.css'; +@import '~codemirror/theme/dracula.css'; + +.code-mirror-wrapper .CodeMirror { + position: absolute; + height: calc(100% - 24px); + width: 100%; +} + +.ant-pro-grid-content.wide { + max-width: unset; + height: calc(100vh - 72px); + overflow: auto; + .ant-pro-page-container-children-content{ + overflow: auto; + } +} diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx new file mode 100644 index 00000000000..1c0d70adf9c --- /dev/null +++ b/src/layouts/index.tsx @@ -0,0 +1,54 @@ +import React, { useEffect, useState } from 'react'; +import { Button, Descriptions, Result, Avatar, Space, Statistic } from 'antd'; +import { LikeOutlined, UserOutlined } from '@ant-design/icons'; +import ProLayout, { + PageContainer, + PageLoading, + SettingDrawer, +} from '@ant-design/pro-layout'; +import defaultProps from './defaultProps'; +import { Link, history } from 'umi'; +import config from '@/utils/config'; +import 'codemirror/mode/shell/shell.js' +import './index.less'; + +export default function (props: any) { + useEffect(() => { + const isAuth = localStorage.getItem(config.authKey); + if (!isAuth) { + history.push('/login'); + } + }, []); + useEffect(() => { + if (props.location.pathname === '/') { + history.push('/config'); + } + }, [props.location.pathname]); + if (props.location.pathname === '/login') { + return props.children; + } + return ( + { + if ( + menuItemProps.isUrl || + !menuItemProps.path || + location.pathname === menuItemProps.path + ) { + return defaultDom; + } + return {defaultDom}; + }} + // rightContentRender={() => ( + //
+ // } /> + //
+ // )} + {...defaultProps} + > + {props.children} +
+ ); +} diff --git a/src/pages/code/index.less b/src/pages/code/index.less new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pages/code/index.tsx b/src/pages/code/index.tsx new file mode 100644 index 00000000000..69fa96151eb --- /dev/null +++ b/src/pages/code/index.tsx @@ -0,0 +1,93 @@ +import React, { PureComponent, Fragment, useState, useEffect } from 'react'; +import { Button, notification, Modal } from 'antd'; +import config from '@/utils/config'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Controlled as CodeMirror } from 'react-codemirror2'; +import { request } from '@/utils/http'; + +const Crontab = () => { + const [width, setWdith] = useState('100%'); + const [marginLeft, setMarginLeft] = useState(0); + const [marginTop, setMarginTop] = useState(-72); + const [value, setValue] = useState(''); + const [loading, setLoading] = useState(true); + + const getConfig = () => { + setLoading(true); + request.get(`${config.apiPrefix}config/shareCode`).then((data) => { + setValue(data); + }).finally(() => setLoading(false)); + }; + + const updateConfig = () => { + request + .post(`${config.apiPrefix}save`, { + data: { content: value, name: 'diy.sh' }, + }) + .then((data) => { + notification.success({ + message: data.msg, + }); + }); + }; + + useEffect(() => { + if (document.body.clientWidth < 768) { + setWdith('auto'); + setMarginLeft(0); + setMarginTop(0); + } else { + setWdith('100%'); + setMarginLeft(0); + setMarginTop(-72); + } + getConfig(); + }, []); + + return ( + + 保存 + , + ]} + header={{ + style: { + padding: '4px 16px 4px 15px', + position: 'sticky', + top: 0, + left: 0, + zIndex: 20, + marginTop, + width, + marginLeft, + }, + }} + style={{ + height: '100vh', + }} + > + { + setValue(value); + }} + onChange={(editor, data, value) => {}} + /> + + ); +}; + +export default Crontab; diff --git a/src/pages/config/index.less b/src/pages/config/index.less new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pages/config/index.tsx b/src/pages/config/index.tsx new file mode 100644 index 00000000000..39b56382070 --- /dev/null +++ b/src/pages/config/index.tsx @@ -0,0 +1,148 @@ +import React, { PureComponent, Fragment, useState, useEffect } from 'react'; +import { Button, notification, Modal } from 'antd'; +import config from '@/utils/config'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Controlled as CodeMirror } from 'react-codemirror2'; +import { request } from '@/utils/http'; +import QRCode from 'qrcode.react'; + +const Config = () => { + const [width, setWdith] = useState('100%'); + const [marginLeft, setMarginLeft] = useState(0); + const [marginTop, setMarginTop] = useState(-72); + const [value, setValue] = useState(''); + const [loading, setLoading] = useState(true); + + const getConfig = () => { + setLoading(true); + request.get(`${config.apiPrefix}config/config`).then((data) => { + setValue(data); + }).finally(() => setLoading(false)); + }; + + const updateConfig = () => { + request + .post(`${config.apiPrefix}save`, { + data: { content: value, name: 'config.sh' }, + }) + .then((data) => { + notification.success({ + message: data.msg, + }); + }); + }; + + function sleep(time: number) { + return new Promise((resolve) => setTimeout(resolve, time)); + } + + const showQrCode = () => { + request.get(`${config.apiPrefix}qrcode`).then(async (data) => { + const modal = Modal.info({ + title: '二维码', + content: ( +
+ +
+ ), + }); + getCookie(modal); + }); + }; + + const getCookie = async (modal: { destroy: () => void }) => { + for (let i = 0; i < 50; i++) { + const result = await request.get(`${config.apiPrefix}cookie`); + console.log(i, result); + if (result && result.cookie) { + notification.success({ + message: 'Cookie获取成功', + }); + modal.destroy(); + Modal.success({ + title: '获取Cookie成功', + content:
{result.cookie}
, + }); + break; + } + await sleep(2000); + } + }; + + useEffect(() => { + if (document.body.clientWidth < 768) { + setWdith('auto'); + setMarginLeft(0); + setMarginTop(0); + } else { + setWdith('100%'); + setMarginLeft(0); + setMarginTop(-72); + } + getConfig(); + }, []); + + return ( + + 保存 + , + , + ]} + header={{ + style: { + padding: '4px 16px 4px 15px', + position: 'sticky', + top: 0, + left: 0, + zIndex: 20, + marginTop, + width, + marginLeft, + }, + }} + style={{ + height: '100vh', + }} + > + { + setValue(value); + }} + onChange={(editor, data, value) => {}} + /> + + ); +}; + +export default Config; diff --git a/src/pages/cookie/index.less b/src/pages/cookie/index.less new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pages/cookie/index.tsx b/src/pages/cookie/index.tsx new file mode 100644 index 00000000000..feeadecf99a --- /dev/null +++ b/src/pages/cookie/index.tsx @@ -0,0 +1,190 @@ +import React, { PureComponent, Fragment, useState, useEffect } from 'react'; +import { Button, notification, Modal, Table, Tag, Space } from 'antd'; +import config from '@/utils/config'; +import { PageContainer } from '@ant-design/pro-layout'; +import { request } from '@/utils/http'; +import QRCode from 'qrcode.react'; + +const columns = [ + { + title: '用户名', + dataIndex: 'pin', + key: 'pin', + }, + { + title: '昵称', + dataIndex: 'nickname', + key: 'nickname', + }, + { + title: '值', + dataIndex: 'cookie', + key: 'cookie', + }, + { + title: '状态', + key: 'status', + dataIndex: 'status', + render: (text: string, record: any) => ( + success + ), + }, + { + title: '操作', + key: 'action', + render: (text: string, record: any) => ( + +
Invite {record.name} + Delete + + ), + }, +]; + +const data = [ + { + key: '1', + name: 'John Brown', + age: 32, + address: 'New York No. 1 Lake Park', + tags: ['nice', 'developer'], + }, + { + key: '2', + name: 'Jim Green', + age: 42, + address: 'London No. 1 Lake Park', + tags: ['loser'], + }, + { + key: '3', + name: 'Joe Black', + age: 32, + address: 'Sidney No. 1 Lake Park', + tags: ['cool', 'teacher'], + }, +]; + +const Config = () => { + const [width, setWdith] = useState('100%'); + const [marginLeft, setMarginLeft] = useState(0); + const [marginTop, setMarginTop] = useState(-72); + const [value, setValue] = useState(''); + const [loading, setLoading] = useState(true); + + const getConfig = () => { + setLoading(true); + request.get(`${config.apiPrefix}config/config`).then((data) => { + setValue(data); + }).finally(() => setLoading(false)); + }; + + const updateConfig = () => { + request + .post(`${config.apiPrefix}save`, { + data: { content: value, name: 'config.sh' }, + }) + .then((data) => { + notification.success({ + message: data.msg, + }); + }); + }; + + function sleep(time: number) { + return new Promise((resolve) => setTimeout(resolve, time)); + } + + const showQrCode = () => { + request.get(`${config.apiPrefix}qrcode`).then(async (data) => { + const modal = Modal.info({ + title: '二维码', + content: ( +
+ +
+ ), + }); + getCookie(modal); + }); + }; + + const getCookie = async (modal: { destroy: () => void }) => { + for (let i = 0; i < 50; i++) { + const result = await request.get(`${config.apiPrefix}cookie`); + console.log(i, result); + if (result && result.cookie) { + notification.success({ + message: 'Cookie获取成功', + }); + modal.destroy(); + Modal.success({ + title: '获取Cookie成功', + content:
{result.cookie}
, + }); + break; + } + await sleep(2000); + } + }; + + useEffect(() => { + if (document.body.clientWidth < 768) { + setWdith('auto'); + setMarginLeft(0); + setMarginTop(0); + } else { + setWdith('100%'); + setMarginLeft(0); + setMarginTop(-72); + } + getConfig(); + }, []); + + return ( + + 扫码获取Cookie + , + ]} + header={{ + style: { + padding: '4px 16px 4px 15px', + position: 'sticky', + top: 0, + left: 0, + zIndex: 20, + marginTop, + width, + marginLeft, + }, + }} + style={{ + height: '100vh', + }} + > + + + ); +}; + +export default Config; diff --git a/src/pages/crontab/index.less b/src/pages/crontab/index.less new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pages/crontab/index.tsx b/src/pages/crontab/index.tsx new file mode 100644 index 00000000000..b32ea4fd512 --- /dev/null +++ b/src/pages/crontab/index.tsx @@ -0,0 +1,92 @@ +import React, { PureComponent, Fragment, useState, useEffect } from 'react'; +import { Button, notification, Modal } from 'antd'; +import config from '@/utils/config'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Controlled as CodeMirror } from 'react-codemirror2'; +import { request } from '@/utils/http'; + +const Crontab = () => { + const [width, setWdith] = useState('100%'); + const [marginLeft, setMarginLeft] = useState(0); + const [marginTop, setMarginTop] = useState(-72); + const [value, setValue] = useState(''); + const [loading, setLoading] = useState(true); + + const getConfig = () => { + setLoading(true); + request.get(`${config.apiPrefix}config/crontab`).then((data) => { + setValue(data); + }).finally(() => setLoading(false)); + }; + + const updateConfig = () => { + request + .post(`${config.apiPrefix}save`, { + data: { content: value, name: 'crontab.list' }, + }) + .then((data) => { + notification.success({ + message: data.msg, + }); + }); + }; + + useEffect(() => { + if (document.body.clientWidth < 768) { + setWdith('auto'); + setMarginLeft(0); + setMarginTop(0); + } else { + setWdith('100%'); + setMarginLeft(0); + setMarginTop(-72); + } + getConfig(); + }, []); + + return ( + + 保存 + , + ]} + header={{ + style: { + padding: '4px 16px 4px 15px', + position: 'sticky', + top: 0, + left: 0, + zIndex: 20, + marginTop, + width, + marginLeft, + }, + }} + style={{ + height: '100vh', + }} + > + { + setValue(value); + }} + onChange={(editor, data, value) => {}} + /> + + ); +}; + +export default Crontab; diff --git a/src/pages/diff/index.less b/src/pages/diff/index.less new file mode 100644 index 00000000000..a0acd042e71 --- /dev/null +++ b/src/pages/diff/index.less @@ -0,0 +1,12 @@ +.d2h-files-diff { + height: calc(100vh - 130px); + overflow: auto; +} + +.d2h-code-side-linenumber { + position: relative; +} + +.d2h-code-side-line { + padding: 0 0.5em; +} \ No newline at end of file diff --git a/src/pages/diff/index.tsx b/src/pages/diff/index.tsx new file mode 100644 index 00000000000..970f365db9a --- /dev/null +++ b/src/pages/diff/index.tsx @@ -0,0 +1,93 @@ +import React, { PureComponent, Fragment, useState, useEffect } from 'react'; +import { Button, notification, Modal } from 'antd'; +import config from '@/utils/config'; +import { PageContainer } from '@ant-design/pro-layout'; +import { request } from '@/utils/http'; +import ReactDiffViewer from 'react-diff-viewer'; +import './index.less'; + +const Crontab = () => { + const [width, setWdith] = useState('100%'); + const [marginLeft, setMarginLeft] = useState(0); + const [marginTop, setMarginTop] = useState(-72); + const [value, setValue] = useState(''); + const [sample, setSample] = useState(''); + const [loading, setLoading] = useState(true); + + const getConfig = () => { + request.get(`${config.apiPrefix}config/config`).then((data) => { + setValue(data); + }); + }; + + const getSample = () => { + setLoading(true); + request.get(`${config.apiPrefix}config/sample`).then((data) => { + setSample(data); + }).finally(() => setLoading(false)); + }; + + useEffect(() => { + if (document.body.clientWidth < 768) { + setWdith('auto'); + setMarginLeft(0); + setMarginTop(0); + } else { + setWdith('100%'); + setMarginLeft(0); + setMarginTop(-72); + } + getConfig(); + getSample(); + }, []); + + return ( + + + {/* */} + + ); +}; + +export default Crontab; diff --git a/src/pages/diy/index.less b/src/pages/diy/index.less new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pages/diy/index.tsx b/src/pages/diy/index.tsx new file mode 100644 index 00000000000..812063d9852 --- /dev/null +++ b/src/pages/diy/index.tsx @@ -0,0 +1,92 @@ +import React, { PureComponent, Fragment, useState, useEffect } from 'react'; +import { Button, notification, Modal } from 'antd'; +import config from '@/utils/config'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Controlled as CodeMirror } from 'react-codemirror2'; +import { request } from '@/utils/http'; + +const Crontab = () => { + const [width, setWdith] = useState('100%'); + const [marginLeft, setMarginLeft] = useState(0); + const [marginTop, setMarginTop] = useState(-72); + const [value, setValue] = useState(''); + const [loading, setLoading] = useState(true); + + const getConfig = () => { + setLoading(true); + request.get(`${config.apiPrefix}config/diy`).then((data) => { + setValue(data); + }).finally(() => setLoading(false)); + }; + + const updateConfig = () => { + request + .post(`${config.apiPrefix}save`, { + data: { content: value, name: 'diy.sh' }, + }) + .then((data) => { + notification.success({ + message: data.msg, + }); + }); + }; + + useEffect(() => { + if (document.body.clientWidth < 768) { + setWdith('auto'); + setMarginLeft(0); + setMarginTop(0); + } else { + setWdith('100%'); + setMarginLeft(0); + setMarginTop(-72); + } + getConfig(); + }, []); + + return ( + + 保存 + , + ]} + header={{ + style: { + padding: '4px 16px 4px 15px', + position: 'sticky', + top: 0, + left: 0, + zIndex: 20, + marginTop, + width, + marginLeft, + }, + }} + style={{ + height: '100vh', + }} + > + { + setValue(value); + }} + onChange={(editor, data, value) => {}} + /> + + ); +}; + +export default Crontab; diff --git a/src/pages/log/index.less b/src/pages/log/index.less new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pages/log/index.tsx b/src/pages/log/index.tsx new file mode 100644 index 00000000000..0886e3aa5ae --- /dev/null +++ b/src/pages/log/index.tsx @@ -0,0 +1,112 @@ +import React, { PureComponent, Fragment, useState, useEffect } from 'react'; +import { Button, notification, Modal, TreeSelect } from 'antd'; +import config from '@/utils/config'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Controlled as CodeMirror } from 'react-codemirror2'; +import { request } from '@/utils/http'; +const Log = () => { + const [width, setWdith] = useState('100%'); + const [marginLeft, setMarginLeft] = useState(0); + const [marginTop, setMarginTop] = useState(-72); + const [title, setTitle] = useState('log'); + const [value, setValue] = useState('请选择日志文件'); + const [select, setSelect] = useState(); + const [data, setData] = useState(); + const [loading, setLoading] = useState(false); + + const getConfig = () => { + request.get(`${config.apiPrefix}logs`).then((data) => { + setData(formatData(data.dirs) as any); + }); + }; + + const formatData = (tree: any[]) => { + return tree.map(x => { + x.title = x.dirName; + x.value = x.dirName; + x.disabled = true; + x.children = x.files.map((y: string) => ({ title: y, key: y, value: y, parent: x.dirName })); + return x; + }) + } + + const getLog = (node: any) => { + setLoading(true); + request.get(`${config.apiPrefix}logs/${node.parent}/${node.value}`).then((data) => { + setValue(data); + }).finally(() => setLoading(false)); + }; + + const onSelect = (value: any, node: any) => { + setSelect(value); + setTitle(node.parent); + getLog(node); + }; + + useEffect(() => { + if (document.body.clientWidth < 768) { + setWdith('auto'); + setMarginLeft(0); + setMarginTop(0); + } else { + setWdith('100%'); + setMarginLeft(0); + setMarginTop(-72); + } + getConfig(); + }, []); + + return ( + + ]} + header={{ + style: { + padding: '4px 16px 4px 15px', + position: 'sticky', + top: 0, + left: 0, + zIndex: 20, + marginTop, + width, + marginLeft, + }, + }} + style={{ + height: '100vh', + }} + > + { + setValue(value); + }} + onChange={(editor, data, value) => {}} + /> + + ); +}; + +export default Log; diff --git a/src/pages/login/index.less b/src/pages/login/index.less new file mode 100644 index 00000000000..d031b7a0250 --- /dev/null +++ b/src/pages/login/index.less @@ -0,0 +1,58 @@ +.form { + position: absolute; + top: 45%; + left: 50%; + margin: -160px 0 0 -160px; + width: 320px; + height: 320px; + padding: 36px; + box-shadow: 0 0 100px rgba(0, 0, 0, 0.08); + + button { + width: 100%; + } + + p { + color: rgb(204, 204, 204); + text-align: center; + margin-top: 16px; + font-size: 12px; + display: flex; + justify-content: space-between; + } +} + +.logo { + text-align: center; + cursor: pointer; + margin-bottom: 24px; + display: flex; + justify-content: center; + align-items: center; + + img { + width: 40px; + margin-right: 8px; + } + + span { + vertical-align: text-bottom; + font-size: 16px; + text-transform: uppercase; + display: inline-block; + font-weight: 700; + // color: @primary-color; + // .text-gradient(); + } +} + +.ant-spin-container, +.ant-spin-nested-loading { + height: 100%; +} + +.footer { + position: absolute; + width: 100%; + bottom: 0; +} \ No newline at end of file diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx new file mode 100644 index 00000000000..b82e5a57c09 --- /dev/null +++ b/src/pages/login/index.tsx @@ -0,0 +1,73 @@ +import React, { Fragment, useEffect } from 'react'; +import { Button, Row, Input, Form, notification } from 'antd'; +import config from '@/utils/config'; +import { history } from 'umi'; +import styles from './index.less'; +import { request } from '@/utils/http'; + +const FormItem = Form.Item; + +const Login = () => { + const handleOk = (values: any) => { + request + .post(`${config.apiPrefix}auth`, { + data: { + username: values.username, + password: values.password, + }, + }) + .then((data) => { + if (data.err == 0) { + localStorage.setItem(config.authKey, 'true'); + history.push('/cookie'); + } else { + notification.open({ + message: data.msg, + }); + } + }) + .catch(function (error) { + console.log(error); + }); + }; + + useEffect(() => { + const isAuth = localStorage.getItem(config.authKey); + if (isAuth) { + history.push('/cookie'); + } + }, []) + + return ( + +
+
+ {config.siteName} +
+
+ + + + + + + + + + +
+
+ ); +}; + +export default Login; diff --git a/src/pages/password/index.less b/src/pages/password/index.less new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pages/password/index.tsx b/src/pages/password/index.tsx new file mode 100644 index 00000000000..08c39107b48 --- /dev/null +++ b/src/pages/password/index.tsx @@ -0,0 +1,84 @@ +import React, { PureComponent, Fragment, useState, useEffect } from 'react'; +import { Button, notification, Input, Form } from 'antd'; +import config from '@/utils/config'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Controlled as CodeMirror } from 'react-codemirror2'; +import { request } from '@/utils/http'; + +const Password = () => { + const [width, setWdith] = useState('100%'); + const [marginLeft, setMarginLeft] = useState(0); + const [marginTop, setMarginTop] = useState(-72); + const [value, setValue] = useState(''); + const [loading, setLoading] = useState(true); + + const handleOk = (values: any) => { + request + .post(`${config.apiPrefix}auth?t=${Date.now()}`, { + data: { + username: values.username, + password: values.password, + }, + }) + .then((data) => { + if (data.err == 0) { + localStorage.setItem(config.authKey, 'true'); + } else { + notification.open({ + message: data.msg, + }); + } + }) + .catch(function (error) { + console.log(error); + }); + }; + + useEffect(() => { + if (document.body.clientWidth < 768) { + setWdith('auto'); + setMarginLeft(0); + setMarginTop(0); + } else { + setWdith('100%'); + setMarginLeft(0); + setMarginTop(-72); + } + }, []); + + return ( + +
+ + + + + + + + +
+ ); +}; + +export default Password; diff --git a/src/utils/config.ts b/src/utils/config.ts new file mode 100644 index 00000000000..819af0daa53 --- /dev/null +++ b/src/utils/config.ts @@ -0,0 +1,37 @@ +export default { + siteName: '京东羊毛脚本控制面板', + apiPrefix: '/api/', + authKey: 'whyour', + + /* Layout configuration, specify which layout to use for route. */ + layouts: [ + { + name: 'primary', + include: [/.*/], + exclude: [/(\/(en|zh))*\/login/], + }, + ], + + /* I18n configuration, `languages` and `defaultLanguage` are required currently. */ + i18n: { + /* Countrys flags: https://www.flaticon.com/packs/countrys-flags */ + languages: [ + { + key: 'pt-br', + title: 'Português', + flag: '/portugal.svg', + }, + { + key: 'en', + title: 'English', + flag: '/america.svg', + }, + { + key: 'zh', + title: '中文', + flag: '/china.svg', + }, + ], + defaultLanguage: 'en', + }, +}; diff --git a/src/utils/http.ts b/src/utils/http.ts new file mode 100644 index 00000000000..58a5377216f --- /dev/null +++ b/src/utils/http.ts @@ -0,0 +1,29 @@ +import { extend } from 'umi-request'; +import { history } from 'umi'; + +const time = Date.now(); +const errorHandler = function (error: any) { + if (error.response) { + console.log(error.response) + } else { + console.log(error.message); + } + + throw error; // 如果throw. 错误将继续抛出. + // return {some: 'data'}; +}; + +const _request = extend({ timeout: 5000, params: { t: time }, errorHandler }); + +_request.interceptors.response.use(async response => { + const res = await response.clone().text() + if (res === '请先登录!') { + setTimeout(() => { + localStorage.removeItem('whyour'); + history.push('/login'); + }); + } + return response; +}) + +export const request = _request; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..6d8f1937015 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "importHelpers": true, + "jsx": "react-jsx", + "esModuleInterop": true, + "sourceMap": true, + "baseUrl": "./", + "strict": true, + "paths": { + "@/*": ["src/*"], + "@@/*": ["src/.umi/*"] + }, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true + }, + "include": [ + "mock/**/*", + "src/**/*", + "config/**/*", + ".umirc.ts", + "typings.d.ts" + ], + "exclude": [ + "node_modules", + "lib", + "es", + "dist", + "typings", + "**/__test__", + "test", + "docs", + "tests" + ] +} diff --git a/typings.d.ts b/typings.d.ts new file mode 100644 index 00000000000..4b2efc88fc2 --- /dev/null +++ b/typings.d.ts @@ -0,0 +1,8 @@ +declare module '*.css'; +declare module '*.less'; +declare module '*.png'; +declare module '*.svg' { + export function ReactComponent(props: React.SVGProps): React.ReactElement + const url: string + export default url +} diff --git a/update.js b/update.js new file mode 100755 index 00000000000..1e1cf4c2083 --- /dev/null +++ b/update.js @@ -0,0 +1,42 @@ +/* + * @Author: lxk0301 https://github.com/lxk0301 + * @Date: 2020-12-20 13:50:34 + * @Last Modified by: lxk0301 + * @Last Modified time: 2020-12-20 13:51:02 + */ +const $ = new Env('通知'); +const notify = require('./scripts/sendNotify'); +const fs = require('fs'); +!(async() => { + await update(); +})() + .catch((e) => $.logErr(e)) + .finally(() => $.done()) + +async function update() { + try { + if (fs.existsSync('new_task')) { + const newTaskContent = await fs.readFileSync('./new_task', 'utf8'); + if (newTaskContent) { + await notify.sendNotify('新增薅羊毛任务通知', newTaskContent); + } + } + + if (fs.existsSync('drop_task')) { + const dropTaskContent = await fs.readFileSync('./drop_task', 'utf8'); + if (dropTaskContent) { + await notify.sendNotify('删除失效任务通知', dropTaskContent); + } + } + + if (fs.existsSync('version')) { + const versionContent = await fs.readFileSync('./version', 'utf8'); + if (versionContent) { + await notify.sendNotify('配置文件更新通知', versionContent); + } + } + } catch (err) { + console.error(err) + } +} +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)} \ No newline at end of file