commit f1f8ece8a20025470d3b702e52b82829dbd5bd94 Author: whyour Date: Sun Mar 14 22:06:27 2021 +0800 初始化项目 diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 00000000..7e3649ac --- /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 00000000..bfb0df96 --- /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 00000000..0d4222f5 --- /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 00000000..94beb148 --- /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 00000000..bb9e8278 --- /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 00000000..7e7d5b85 --- /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 00000000..ed8488ee --- /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 00000000..2d32145d --- /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 00000000..07ceedf8 --- /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 00000000..b7143d9c --- /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 00000000..34d7c932 --- /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 00000000..565c2902 --- /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 00000000..65540d07 --- /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 00000000..e0069c10 --- /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 00000000..096c16cb Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/layouts/defaultProps.tsx b/src/layouts/defaultProps.tsx new file mode 100644 index 00000000..33fd285f --- /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 00000000..77d80496 --- /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 00000000..1c0d70ad --- /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 00000000..e69de29b diff --git a/src/pages/code/index.tsx b/src/pages/code/index.tsx new file mode 100644 index 00000000..69fa9615 --- /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 00000000..e69de29b diff --git a/src/pages/config/index.tsx b/src/pages/config/index.tsx new file mode 100644 index 00000000..39b56382 --- /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 00000000..e69de29b diff --git a/src/pages/cookie/index.tsx b/src/pages/cookie/index.tsx new file mode 100644 index 00000000..feeadecf --- /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 00000000..e69de29b diff --git a/src/pages/crontab/index.tsx b/src/pages/crontab/index.tsx new file mode 100644 index 00000000..b32ea4fd --- /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 00000000..a0acd042 --- /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 00000000..970f365d --- /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 00000000..e69de29b diff --git a/src/pages/diy/index.tsx b/src/pages/diy/index.tsx new file mode 100644 index 00000000..812063d9 --- /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 00000000..e69de29b diff --git a/src/pages/log/index.tsx b/src/pages/log/index.tsx new file mode 100644 index 00000000..0886e3aa --- /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 00000000..d031b7a0 --- /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 00000000..b82e5a57 --- /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 00000000..e69de29b diff --git a/src/pages/password/index.tsx b/src/pages/password/index.tsx new file mode 100644 index 00000000..08c39107 --- /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 00000000..819af0da --- /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 00000000..58a53772 --- /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 00000000..6d8f1937 --- /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 00000000..4b2efc88 --- /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 00000000..1e1cf4c2 --- /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