From b8bd9f447764efcf9566089c7701d74fd60f6b6b Mon Sep 17 00:00:00 2001 From: KingRan Date: Thu, 27 Jul 2023 22:55:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +- docker/Dockerfile | 37 + docker/Readme.md | 264 ++++ docker/auto_help.sh | 155 +++ docker/bot/jd.png | Bin 0 -> 7335 bytes docker/bot/jd_bot | 1114 +++++++++++++++++ docker/bot/requirements.txt | 5 + docker/bot/setup.py | 13 + docker/crontab_list.sh | 101 ++ docker/default_task.sh | 252 ++++ docker/docker_entrypoint.sh | 57 + docker/example/custom-append.yml | 62 + docker/example/custom-overwrite.yml | 62 + docker/example/default.yml | 59 + .../docker多账户使用独立容器使用说明.md | 83 ++ .../jd_scripts.custom-append.syno.json | 65 + .../jd_scripts.custom-overwrite.syno.json | 69 + docker/example/jd_scripts.syno.json | 83 ++ docker/example/multi.yml | 62 + docker/notify_docker_user.js | 20 + docker/proc_file.sh | 27 + jd_wsck.py | 412 ++++++ jd_wskey.py | 680 ---------- 23 files changed, 3010 insertions(+), 681 deletions(-) create mode 100644 docker/Dockerfile create mode 100644 docker/Readme.md create mode 100644 docker/auto_help.sh create mode 100644 docker/bot/jd.png create mode 100644 docker/bot/jd_bot create mode 100644 docker/bot/requirements.txt create mode 100644 docker/bot/setup.py create mode 100644 docker/crontab_list.sh create mode 100644 docker/default_task.sh create mode 100644 docker/docker_entrypoint.sh create mode 100644 docker/example/custom-append.yml create mode 100644 docker/example/custom-overwrite.yml create mode 100644 docker/example/default.yml create mode 100644 docker/example/docker多账户使用独立容器使用说明.md create mode 100644 docker/example/jd_scripts.custom-append.syno.json create mode 100644 docker/example/jd_scripts.custom-overwrite.syno.json create mode 100644 docker/example/jd_scripts.syno.json create mode 100644 docker/example/multi.yml create mode 100644 docker/notify_docker_user.js create mode 100644 docker/proc_file.sh create mode 100644 jd_wsck.py delete mode 100644 jd_wskey.py diff --git a/README.md b/README.md index 49ae51d..d639b5e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ql repo命令拉取脚本时需要拉取的文件后缀,直接写文件后缀 `RepoFileExtensions="js py ts"` -### 神秘代码 +### 青龙面板神秘代码 `ql repo https://github.com/KingRan/KR.git "jd_|jx_|jdCookie" "activity|backUp" "^jd[^_]|USER|utils|function|sign|sendNotify|ql|JDJR"` @@ -25,4 +25,11 @@ ql repo命令拉取脚本时需要拉取的文件后缀,直接写文件后缀 `rm -rf /ql/repo/KingRan_KR && ql repo https://github.com/KingRan/KR.git "jd_|jx_|jdCookie" "activity|backUp" "^jd[^_]|USER|utils|function|sendNotify|ql|JDJR"` +### Arcadia神秘代码 + +`arcadia repo KR "https://github.com/KingRan/KR.git" main \ +--updateTaskList true --whiteList "^jd_|^jx_" \ +--blackList "wskey|CheckCK|^jd_(disable|wx|txzj_|opencard|lzkj_|dplh|jinggeng` + + diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..b4dfea7 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,37 @@ +FROM node:lts-alpine3.12 + +LABEL AUTHOR="none" \ + VERSION=0.1.4 + +ARG KEY="-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn\nNhAAAAAwEAAQAAAQEAvRQk2oQqLB01iVnJKrnI3tTfJyEHzc2ULVor4vBrKKWOum4dbTeT\ndNWL5aS+CJso7scJT3BRq5fYVZcz5ra0MLMdQyFL1DdwurmzkhPYbwcNrJrB8abEPJ8ltS\nMoa0X9ecmSepaQFedZOZ2YeT/6AAXY+cc6xcwyuRVQ2ZJ3YIMBrRuVkF6nYwLyBLFegzhu\nJJeU5o53kfpbTCirwK0h9ZsYwbNbXYbWuJHmtl5tEBf2Hz+5eCkigXRq8EhRZlSnXfhPr2\n32VCb1A/gav2/YEaMPSibuBCzqVMVruP5D625XkxMdBdLqLBGWt7bCas7/zH2bf+q3zac4\nLcIFhkC6XwAAA9BjE3IGYxNyBgAAAAdzc2gtcnNhAAABAQC9FCTahCosHTWJWckqucje1N\n8nIQfNzZQtWivi8GsopY66bh1tN5N01YvlpL4ImyjuxwlPcFGrl9hVlzPmtrQwsx1DIUvU\nN3C6ubOSE9hvBw2smsHxpsQ8nyW1IyhrRf15yZJ6lpAV51k5nZh5P/oABdj5xzrFzDK5FV\nDZkndggwGtG5WQXqdjAvIEsV6DOG4kl5TmjneR+ltMKKvArSH1mxjBs1tdhta4kea2Xm0Q\nF/YfP7l4KSKBdGrwSFFmVKdd+E+vbfZUJvUD+Bq/b9gRow9KJu4ELOpUxWu4/kPrbleTEx\n0F0uosEZa3tsJqzv/MfZt/6rfNpzgtwgWGQLpfAAAAAwEAAQAAAQEAnMKZt22CBWcGHuUI\nytqTNmPoy2kwLim2I0+yOQm43k88oUZwMT+1ilUOEoveXgY+DpGIH4twusI+wt+EUVDC3e\nlyZlixpLV+SeFyhrbbZ1nCtYrtJutroRMVUTNf7GhvucwsHGS9+tr+96y4YDZxkBlJBfVu\nvdUJbLfGe0xamvE114QaZdbmKmtkHaMQJOUC6EFJI4JmSNLJTxNAXKIi3TUrS7HnsO3Xfv\nhDHElzSEewIC1smwLahS6zi2uwP1ih4fGpJJbU6FF/jMvHf/yByHDtdcuacuTcU798qT0q\nAaYlgMd9zrLC1OHMgSDcoz9/NQTi2AXGAdo4N+mnxPTHcQAAAIB5XCz1vYVwJ8bKqBelf1\nw7OlN0QDM4AUdHdzTB/mVrpMmAnCKV20fyA441NzQZe/52fMASUgNT1dQbIWCtDU2v1cP6\ncG8uyhJOK+AaFeDJ6NIk//d7o73HNxR+gCCGacleuZSEU6075Or2HVGHWweRYF9hbmDzZb\nCLw6NsYaP2uAAAAIEA3t1BpGHHek4rXNjl6d2pI9Pyp/PCYM43344J+f6Ndg3kX+y03Mgu\n06o33etzyNuDTslyZzcYUQqPMBuycsEb+o5CZPtNh+1klAVE3aDeHZE5N5HrJW3fkD4EZw\nmOUWnRj1RT2TsLwixB21EHVm7fh8Kys1d2ULw54LVmtv4+O3cAAACBANkw7XZaZ/xObHC9\n1PlT6vyWg9qHAmnjixDhqmXnS5Iu8TaKXhbXZFg8gvLgduGxH/sGwSEB5D6sImyY+DW/OF\nbmIVC4hwDUbCsTMsmTTTgyESwmuQ++JCh6f2Ams1vDKbi+nOVyqRvCrAHtlpaqSfv8hkjK\npBBqa/rO5yyYmeJZAAAAFHJvb3RAbmFzLmV2aW5lLnByZXNzAQIDBAUG\n-----END OPENSSH PRIVATE KEY-----" + +ENV DEFAULT_LIST_FILE=crontab_list.sh \ + CUSTOM_LIST_MERGE_TYPE=append \ + COOKIES_LIST=/scripts/logs/cookies.list \ + REPO_URL=git@gitee.com:lxk0301/jd_scripts.git \ + REPO_BRANCH=master + +RUN set -ex \ + && apk update \ + && apk upgrade \ + && apk add --no-cache bash tzdata git moreutils curl jq openssh-client \ + && rm -rf /var/cache/apk/* \ + && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone \ + && mkdir -p /root/.ssh \ + && echo -e $KEY > /root/.ssh/id_rsa \ + && chmod 600 /root/.ssh/id_rsa \ + && ssh-keyscan gitee.com > /root/.ssh/known_hosts \ + && git clone -b $REPO_BRANCH $REPO_URL /scripts \ + && cd /scripts \ + && mkdir logs \ + && npm config set registry https://registry.npm.taobao.org \ + && npm install \ + && cp /scripts/docker/docker_entrypoint.sh /usr/local/bin \ + && chmod +x /usr/local/bin/docker_entrypoint.sh + +WORKDIR /scripts + +ENTRYPOINT ["docker_entrypoint.sh"] + +CMD [ "crond" ] \ No newline at end of file diff --git a/docker/Readme.md b/docker/Readme.md new file mode 100644 index 0000000..c216307 --- /dev/null +++ b/docker/Readme.md @@ -0,0 +1,264 @@ +![Docker Pulls](https://img.shields.io/docker/pulls/lxk0301/jd_scripts?style=for-the-badge) +### Usage +```diff ++ 2021-03-21更新 增加bot交互,spnode指令,功能是否开启自动根据你的配置判断,详见 https://gitee.com/lxk0301/jd_docker/pulls/18 + **bot交互启动前置条件为 配置telegram通知,并且未使用自己代理的 TG_API_HOST** + **spnode使用前置条件未启动bot交互** _(后续可能去掉该限制_ + 使用bot交互+spnode后 后续用户的cookie维护更新只需要更新logs/cookies.conf即可 + 使用bot交互+spnode后 后续执行脚本命令请使用spnode否者无法使用logs/cookies.conf的cookies执行脚本,定时任务也将自动替换为spnode命令执行 + 发送/spnode给bot获取可执行脚本的列表,选择对应的按钮执行。(拓展使用:运行指定路径脚本,例:/spnode /scripts/jd_818.js) + spnode功能概述示例 + spnode conc /scripts/jd_bean_change.js 为每个cookie单独执行jd_bean_change脚本(伪并发 + spnode 1 /scripts/jd_bean_change.js 为logs/cookies.conf文件里面第一行cookie账户单独执行jd_bean_change脚本 + spnode jd_XXXX /scripts/jd_bean_change.js 为logs/cookies.conf文件里面pt_pin=jd_XXXX的cookie账户单独执行jd_bean_change脚本 + spnode /scripts/jd_bean_change.js 为logs/cookies.conf所有cookies账户一起执行jd_bean_change脚本 + +**请仔细阅读并理解上面的内容,使用bot交互默认开启spnode指令功能功能。** ++ 2021-03-9更新 新版docker单容器多账号自动互助 ++开启方式:docker-compose.yml 中添加环境变量 - ENABLE_AUTO_HELP=true ++助力原则:不考虑需要被助力次数与提供助力次数 假设有3个账号,则生成: ”助力码1@助力码2@助力码3&助力码1@助力码2@助力码3&助力码1@助力码2@助力码3“ ++原理说明:1、定时调用 /scripts/docker/auto_help.sh collect 收集各个活动的助力码,整理、去重、排序、保存到 /scripts/logs/sharecodeCollection.log; + 2、(由于linux进程限制,父进程无法获取子进程环境变量)在每次脚本运行前,在当前进程先调用 /scripts/docker/auto_help.sh export 把助力码注入到环境变量 + ++ 2021-02-21更新 https://gitee.com/lxk0301/jd_scripts仓库被迫私有,老用户重新更新一下镜像:https://hub.docker.com/r/lxk0301/jd_scripts)(docker-compose.yml的REPO_URL记得修改)后续可同步更新jd_script仓库最新脚本 ++ 2021-02-10更新 docker-compose里面,填写环境变量 SHARE_CODE_FILE=/scripts/logs/sharecode.log, 多账号可实现自己互助(只限sharecode.log日志里面几个活动),注:已停用,请使用2021-03-9更新 ++ 2021-01-22更新 CUSTOM_LIST_FILE 参数支持远程定时任务列表 (⚠️务必确认列表中的任务在仓库里存在) ++ 例1:配置远程crontab_list.sh, 此处借用 shylocks 大佬的定时任务列表, 本仓库不包含列表中的任务代码, 仅作示范 ++ CUSTOM_LIST_FILE=https://raw.githubusercontent.com/shylocks/Loon/main/docker/crontab_list.sh ++ ++ 例2:配置docker挂载本地定时任务列表, 用法不变, 注意volumes挂载 ++ volumes: ++ - ./my_crontab_list.sh:/scripts/docker/my_crontab_list.sh ++ environment: ++ - CUSTOM_LIST_FILE=my_crontab_list.sh + + ++ 2021-01-21更新 增加 DO_NOT_RUN_SCRIPTS 参数配置不执行的脚本 ++ 例:DO_NOT_RUN_SCRIPTS=jd_family.js&jd_dreamFactory.js&jd_jxnc.js +建议填写完整文件名,不完整的文件名可能导致其他脚本被禁用。 +例如:“jd_joy”会匹配到“jd_joy_feedPets”、“jd_joy_reward”、“jd_joy_steal” + ++ 2021-01-03更新 增加 CUSTOM_SHELL_FILE 参数配置执行自定义shell脚本 ++ 例1:配置远程shell脚本, 我自己写了一个shell脚本https://raw.githubusercontent.com/iouAkira/someDockerfile/master/jd_scripts/shell_script_mod.sh 内容很简单下载惊喜农场并添加定时任务 ++ CUSTOM_SHELL_FILE=https://raw.githubusercontent.com/iouAkira/someDockerfile/master/jd_scripts/shell_script_mod.sh ++ ++ 例2:配置docker挂载本地自定义shell脚本,/scripts/docker/shell_script_mod.sh 为你在docker-compose.yml里面挂载到容器里面绝对路径 ++ CUSTOM_SHELL_FILE=/scripts/docker/shell_script_mod.sh ++ ++ tip:如果使用远程自定义,请保证网络畅通或者选择合适的国内仓库,例如有部分人的容器里面就下载不到github的raw文件,那就可以把自己的自定义shell写在gitee上,或者换本地挂载 ++ 如果是 docker 挂载本地,请保证文件挂载进去了,并且配置的是绝对路径。 ++ 自定义 shell 脚本里面如果要加 crontab 任务请使用 echo 追加到 /scripts/docker/merged_list_file.sh 里面否则不生效 ++ 注⚠️ 建议无shell能力的不要轻易使用,当然你可以找别人写好适配了这个docker镜像的脚本直接远程配置 ++ 上面写了这么多如果还看不懂,不建议使用该变量功能。 +_____ +! ⚠️⚠️⚠️2020-12-11更新镜像启动方式,虽然兼容旧版的运行启动方式,但是强烈建议更新镜像和配置后使用 +! 更新后`command:`指令配置不再需要 +! 更新后可以使用自定义任务文件追加在默任务文件之后,比以前的完全覆盖多一个选择 +! - 新的自定两个环境变量为 `CUSTOM_LIST_MERGE_TYPE`:自定文件的生效方式可选值为`append`,`overwrite`默认为`append` ; `CUSTOM_LIST_FILE`: 自定义文件的名字 +! 更新镜像增减镜像更新通知,以后镜像如果更新之后,会通知用户更新 +``` +> 推荐使用`docker-compose`所以这里只介绍`docker-compose`使用方式 + + + +Docker安装 + +- 国内一键安装 `sudo curl -sSL https://get.daocloud.io/docker | sh` +- 国外一键安装 `sudo curl -sSL get.docker.com | sh` +- 北京外国语大学开源软件镜像站 `https://mirrors.bfsu.edu.cn/help/docker-ce/` + + +docker-compose 安装(群晖`nas docker`自带安装了`docker-compose`) + +``` +sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +sudo chmod +x /usr/local/bin/docker-compose +``` +`Ubuntu`用户快速安装`docker-compose` +``` +sudo apt-get update && sudo apt-get install -y python3-pip curl vim git moreutils +pip3 install --upgrade pip +pip install docker-compose +``` + +### win10用户下载安装[docker desktop](https://www.docker.com/products/docker-desktop) + +通过`docker-compose version`查看`docker-compose`版本,确认是否安装成功。 + + +### 如果需要使用 docker 多个账户独立并发执行定时任务,[参考这里](./example/docker%E5%A4%9A%E8%B4%A6%E6%88%B7%E4%BD%BF%E7%94%A8%E7%8B%AC%E7%AB%8B%E5%AE%B9%E5%99%A8%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md#%E4%BD%BF%E7%94%A8%E6%AD%A4%E6%96%B9%E5%BC%8F%E8%AF%B7%E5%85%88%E7%90%86%E8%A7%A3%E5%AD%A6%E4%BC%9A%E4%BD%BF%E7%94%A8docker%E5%8A%9E%E6%B3%95%E4%B8%80%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F) + +> 注⚠️:前提先理解学会使用这下面的教程 +### 创建一个目录`jd_scripts`用于存放备份配置等数据,迁移重装的时候只需要备份整个jd_scripts目录即可 +需要新建的目录文件结构参考如下: +``` +jd_scripts +├── logs +│   ├── XXXX.log +│   └── XXXX.log +├── my_crontab_list.sh +└── docker-compose.yml +``` +- `jd_scripts/logs`建一个空文件夹就行 +- `jd_scripts/docker-compose.yml` 参考内容如下(自己动手能力不行搞不定请使用默认配置): +- - [使用默认配置用这个](./example/default.yml) +- - [使用自定义任务追加到默认任务之后用这个](./example/custom-append.yml) +- - [使用自定义任务覆盖默认任务用这个](./example/custom-overwrite.yml) +- - [一次启动多容器并发用这个](./example/multi.yml) +- - [使用群晖默认配置用这个](./example/jd_scripts.syno.json) +- - [使用群晖自定义任务追加到默认任务之后用这个](./example/jd_scripts.custom-append.syno.json) +- - [使用群晖自定义任务覆盖默认任务用这个](./example/jd_scripts.custom-overwrite.syno.json) +- `jd_scripts/docker-compose.yml`里面的环境变量(`environment:`)配置[参考这里](../githubAction.md#互助码类环境变量) 和[本文末尾](../docker/Readme.md#docker专属环境变量) + + +- `jd_scripts/my_crontab_list.sh` 参考内容如下,自己根据需要调整增加删除,不熟悉用户推荐使用[默认配置](./crontab_list.sh)里面的内容: + +```shell +# 每3天的23:50分清理一次日志(互助码不清理,proc_file.sh对该文件进行了去重) +50 23 */3 * * find /scripts/logs -name '*.log' | grep -v 'sharecode' | xargs rm -rf + +##############短期活动############## +# 小鸽有礼2(活动时间:2021年1月28日~2021年2月28日) +34 9 * * * node /scripts/jd_xgyl.js >> /scripts/logs/jd_jd_xgyl.log 2>&1 + +#女装盲盒 活动时间:2021-2-19至2021-2-25 +5 7,23 19-25 2 * node /scripts/jd_nzmh.js >> /scripts/logs/jd_nzmh.log 2>&1 + +#京东极速版天天领红包 活动时间:2021-1-18至2021-3-3 +5 0,23 * * * node /scripts/jd_speed_redpocke.js >> /scripts/logs/jd_speed_redpocke.log 2>&1 +##############长期活动############## +# 签到 +3 0,18 * * * cd /scripts && node jd_bean_sign.js >> /scripts/logs/jd_bean_sign.log 2>&1 +# 东东超市兑换奖品 +0,30 0 * * * node /scripts/jd_blueCoin.js >> /scripts/logs/jd_blueCoin.log 2>&1 +# 摇京豆 +0 0 * * * node /scripts/jd_club_lottery.js >> /scripts/logs/jd_club_lottery.log 2>&1 +# 东东农场 +5 6-18/6 * * * node /scripts/jd_fruit.js >> /scripts/logs/jd_fruit.log 2>&1 +# 宠汪汪 +15 */2 * * * node /scripts/jd_joy.js >> /scripts/logs/jd_joy.log 2>&1 +# 宠汪汪喂食 +15 */1 * * * node /scripts/jd_joy_feedPets.js >> /scripts/logs/jd_joy_feedPets.log 2>&1 +# 宠汪汪偷好友积分与狗粮 +13 0-21/3 * * * node /scripts/jd_joy_steal.js >> /scripts/logs/jd_joy_steal.log 2>&1 +# 摇钱树 +0 */2 * * * node /scripts/jd_moneyTree.js >> /scripts/logs/jd_moneyTree.log 2>&1 +# 东东萌宠 +5 6-18/6 * * * node /scripts/jd_pet.js >> /scripts/logs/jd_pet.log 2>&1 +# 京东种豆得豆 +0 7-22/1 * * * node /scripts/jd_plantBean.js >> /scripts/logs/jd_plantBean.log 2>&1 +# 京东全民开红包 +1 1 * * * node /scripts/jd_redPacket.js >> /scripts/logs/jd_redPacket.log 2>&1 +# 进店领豆 +10 0 * * * node /scripts/jd_shop.js >> /scripts/logs/jd_shop.log 2>&1 +# 京东天天加速 +8 */3 * * * node /scripts/jd_speed.js >> /scripts/logs/jd_speed.log 2>&1 +# 东东超市 +11 1-23/5 * * * node /scripts/jd_superMarket.js >> /scripts/logs/jd_superMarket.log 2>&1 +# 取关京东店铺商品 +55 23 * * * node /scripts/jd_unsubscribe.js >> /scripts/logs/jd_unsubscribe.log 2>&1 +# 京豆变动通知 +0 10 * * * node /scripts/jd_bean_change.js >> /scripts/logs/jd_bean_change.log 2>&1 +# 京东抽奖机 +11 1 * * * node /scripts/jd_lotteryMachine.js >> /scripts/logs/jd_lotteryMachine.log 2>&1 +# 京东排行榜 +11 9 * * * node /scripts/jd_rankingList.js >> /scripts/logs/jd_rankingList.log 2>&1 +# 天天提鹅 +18 * * * * node /scripts/jd_daily_egg.js >> /scripts/logs/jd_daily_egg.log 2>&1 +# 金融养猪 +12 * * * * node /scripts/jd_pigPet.js >> /scripts/logs/jd_pigPet.log 2>&1 +# 点点券 +20 0,20 * * * node /scripts/jd_necklace.js >> /scripts/logs/jd_necklace.log 2>&1 +# 京喜工厂 +20 * * * * node /scripts/jd_dreamFactory.js >> /scripts/logs/jd_dreamFactory.log 2>&1 +# 东东小窝 +16 6,23 * * * node /scripts/jd_small_home.js >> /scripts/logs/jd_small_home.log 2>&1 +# 东东工厂 +36 * * * * node /scripts/jd_jdfactory.js >> /scripts/logs/jd_jdfactory.log 2>&1 +# 十元街 +36 8,18 * * * node /scripts/jd_syj.js >> /scripts/logs/jd_syj.log 2>&1 +# 京东快递签到 +23 1 * * * node /scripts/jd_kd.js >> /scripts/logs/jd_kd.log 2>&1 +# 京东汽车(签到满500赛点可兑换500京豆) +0 0 * * * node /scripts/jd_car.js >> /scripts/logs/jd_car.log 2>&1 +# 领京豆额外奖励(每日可获得3京豆) +33 4 * * * node /scripts/jd_bean_home.js >> /scripts/logs/jd_bean_home.log 2>&1 +# 微信小程序京东赚赚 +10 11 * * * node /scripts/jd_jdzz.js >> /scripts/logs/jd_jdzz.log 2>&1 +# 宠汪汪邀请助力 +10 9-20/2 * * * node /scripts/jd_joy_run.js >> /scripts/logs/jd_joy_run.log 2>&1 +# crazyJoy自动每日任务 +10 7 * * * node /scripts/jd_crazy_joy.js >> /scripts/logs/jd_crazy_joy.log 2>&1 +# 京东汽车旅程赛点兑换金豆 +0 0 * * * node /scripts/jd_car_exchange.js >> /scripts/logs/jd_car_exchange.log 2>&1 +# 导到所有互助码 +47 7 * * * node /scripts/jd_get_share_code.js >> /scripts/logs/jd_get_share_code.log 2>&1 +# 口袋书店 +7 8,12,18 * * * node /scripts/jd_bookshop.js >> /scripts/logs/jd_bookshop.log 2>&1 +# 京喜农场 +0 9,12,18 * * * node /scripts/jd_jxnc.js >> /scripts/logs/jd_jxnc.log 2>&1 +# 签到领现金 +27 */4 * * * node /scripts/jd_cash.js >> /scripts/logs/jd_cash.log 2>&1 +# 京喜app签到 +39 7 * * * node /scripts/jx_sign.js >> /scripts/logs/jx_sign.log 2>&1 +# 京东家庭号(暂不知最佳cron) +# */20 * * * * node /scripts/jd_family.js >> /scripts/logs/jd_family.log 2>&1 +# 闪购盲盒 +27 8 * * * node /scripts/jd_sgmh.js >> /scripts/logs/jd_sgmh.log 2>&1 +# 京东秒秒币 +10 7 * * * node /scripts/jd_ms.js >> /scripts/logs/jd_ms.log 2>&1 +#美丽研究院 +1 7,12,19 * * * node /scripts/jd_beauty.js >> /scripts/logs/jd_beauty.log 2>&1 +#京东保价 +1 0,23 * * * node /scripts/jd_price.js >> /scripts/logs/jd_price.log 2>&1 +#京东极速版签到+赚现金任务 +1 1,6 * * * node /scripts/jd_speed_sign.js >> /scripts/logs/jd_speed_sign.log 2>&1 +# 删除优惠券(默认注释,如需要自己开启,如有误删,已删除的券可以在回收站中还原,慎用) +#20 9 * * 6 node /scripts/jd_delCoupon.js >> /scripts/logs/jd_delCoupon.log 2>&1 +``` +> 定时任务命之后,也就是 `>>` 符号之前加上 `|ts` 可在日志每一行前面显示时间,如下图: +> ![image](https://user-images.githubusercontent.com/6993269/99031839-09e04b00-25b3-11eb-8e47-0b6515a282bb.png) +- 目录文件配置好之后在 `jd_scripts`目录执行。 + `docker-compose up -d` 启动(修改docker-compose.yml后需要使用此命令使更改生效); + `docker-compose logs` 打印日志; + `docker-compose logs -f` 打印日志,-f表示跟随日志; + `docker logs -f jd_scripts` 和上面两条相比可以显示汉字; + `docker-compose pull` 更新镜像;多容器用户推荐使用`docker pull lxk0301/jd_scripts`; + `docker-compose stop` 停止容器; + `docker-compose restart` 重启容器; + `docker-compose down` 停止并删除容器; + +- 你可能会用到的命令 + + `docker exec -it jd_scripts /bin/sh -c ". /scripts/docker/auto_help.sh export > /scripts/logs/auto_help_export.log && node /scripts/xxxx.js |ts >> /scripts/logs/xxxx.log 2>&1"` 手动运行一脚本(有自动助力) + + `docker exec -it jd_scripts /bin/sh -c "node /scripts/xxxx.js |ts >> /scripts/logs/xxxx.log 2>&1"` 手动运行一脚本(无自动助力) + + `docker exec -it jd_scripts /bin/sh -c 'env'` 查看设置的环境变量 + + `docker exec -it jd_scripts /bin/sh -c 'crontab -l'` 查看已生效的crontab_list定时器任务 + + `docker exec -it jd_scripts sh -c "git pull"` 手动更新jd_scripts仓库最新脚本(默认已有每天拉取两次的定时任务,不推荐使用) + + `docker exec -it jd_scripts /bin/sh` 仅进入容器命令 + + `rm -rf logs/*.log` 删除logs文件夹里面所有的日志文件(linux) + + `docker exec -it jd_scripts /bin/sh -c ' ls jd_*.js | grep -v jd_crazy_joy_coin.js |xargs -i node {}'` 执行所有定时任务 + +- 如果是群晖用户,在docker注册表搜`jd_scripts`,双击下载映像。 +不需要`docker-compose.yml`,只需建个logs/目录,调整`jd_scripts.syno.json`里面对应的配置值,然后导入json配置新建容器。 +若要自定义`my_crontab_list.sh`,再建个`my_crontab_list.sh`文件,配置参考`jd_scripts.my_crontab_list.syno.json`。 +![image](../icon/qh1.png) + +![image](../icon/qh2.png) + +![image](../icon/qh3.png) + +### DOCKER专属环境变量 + +| Name | 归属 | 属性 | 说明 | +| :---------------: | :------------: | :----: | ------------------------------------------------------------ | +| `CRZAY_JOY_COIN_ENABLE` | 是否jd_crazy_joy_coin挂机 | 非必须 | `docker-compose.yml`文件下填写`CRZAY_JOY_COIN_ENABLE=Y`表示挂机,`CRZAY_JOY_COIN_ENABLE=N`表不挂机 | +| `DO_NOT_RUN_SCRIPTS` | 不执行的脚本 | 非必须 | 例:`docker-compose.yml`文件里面填写`DO_NOT_RUN_SCRIPTS=jd_family.js&jd_dreamFactory.js&jd_jxnc.js`, 建议填写完整脚本名,不完整的文件名可能导致其他脚本被禁用 | +| `ENABLE_AUTO_HELP` | 单容器多账号自动互助 | 非必须 | 例:`docker-compose.yml`文件里面填写`ENABLE_AUTO_HELP=true` | diff --git a/docker/auto_help.sh b/docker/auto_help.sh new file mode 100644 index 0000000..5c29885 --- /dev/null +++ b/docker/auto_help.sh @@ -0,0 +1,155 @@ +#set -e + +#日志路径 +logDir="/scripts/logs" + +# 处理后的log文件 +logFile=${logDir}/sharecodeCollection.log +if [ -n "$1" ]; then + parameter=${1} +else + echo "没有参数" +fi + +# 收集助力码 +collectSharecode() { + if [ -f ${2} ]; then + echo "${1}:清理 ${preLogFile} 中的旧助力码,收集新助力码" + + #删除预处理旧助力码 + if [ -f "${logFile}" ]; then + sed -i '/'"${1}"'/d' ${logFile} + fi + + #收集日志中的互助码 + codes="$(sed -n '/'${1}'.*/'p ${2} | sed 's/京东账号/京东账号 /g' | sed 's/(/ (/g' | sed 's/】/】 /g' | awk '{print $4,$5,$6,$7}' | sort -gk2 | awk '!a[$2" "$3]++{print}')" + + #获取ck文件夹中的pin值集合 + if [ -f "/usr/local/bin/spnode" ]; then + ptpins="$(awk -F "=" '{print $3}' $COOKIES_LIST | awk -F ";" '{print $1}')" + else + ptpins="$(echo $JD_COOKIE | sed "s/[ &]/\\n/g" | sed "/^$/d" | awk -F "=" '{print $3}' | awk -F ";" '{print $1}')" + fi + + + #遍历pt_pin值 + for item in $ptpins; do + #中文pin解码 + if [ ${#item} > 20 ]; then + item="$(printf $(echo -n "$item" | sed 's/\\/\\\\/g;s/\(%\)\([0-9a-fA-F][0-9a-fA-F]\)/\\x\2/g')"\n")" + fi + #根据pin值匹配第一个code结果输出到文件中 + echo "$codes" | grep -m1 $item >> $logFile + done + else + echo "${1}:${2} 文件不存在,不清理 ${logFile} 中的旧助力码" + fi + +} + +# 导出助力码 +exportSharecode() { + if [ -f ${logFile} ]; then + #账号数 + cookiecount=$(echo ${JD_COOKIE} | grep -o pt_key | grep -c pt_key) + if [ -f /usr/local/bin/spnode ]; then + cookiecount=$(cat "$COOKIES_LIST" | grep -o pt_key | grep -c pt_key) + fi + echo "cookie个数:${cookiecount}" + + # 单个账号助力码 + singleSharecode=$(sed -n '/'${1}'.*/'p ${logFile} | awk '{print $4}' | awk '{T=T"@"$1} END {print T}' | awk '{print substr($1,2)}') + # | awk '{print $2,$4}' | sort -g | uniq + # echo "singleSharecode:${singleSharecode}" + + # 拼接多个账号助力码 + num=1 + while [ ${num} -le ${cookiecount} ]; do + local allSharecode=${allSharecode}"&"${singleSharecode} + num=$(expr $num + 1) + done + + allSharecode=$(echo ${allSharecode} | awk '{print substr($1,2)}') + + # echo "${1}:${allSharecode}" + + #判断合成的助力码长度是否大于账号数,不大于,则可知没有助力码 + if [ ${#allSharecode} -gt ${cookiecount} ]; then + echo "${1}:导出助力码" + echo "${3}=${allSharecode}" + export ${3}=${allSharecode} + else + echo "${1}:没有助力码,不导出" + fi + + else + echo "${1}:${logFile} 不存在,不导出助力码" + fi + +} + +#生成助力码 +autoHelp() { + if [ ${parameter} == "collect" ]; then + + # echo "收集助力码" + collectSharecode ${1} ${2} ${3} + + elif [ ${parameter} == "export" ]; then + + # echo "导出助力码" + exportSharecode ${1} ${2} ${3} + fi +} + +#日志需要为这种格式才能自动提取 +#Mar 07 00:15:10 【京东账号1(xxxxxx)的京喜财富岛好友互助码】3B41B250C4A369EE6DCA6834880C0FE0624BAFD83FC03CA26F8DEC7DB95D658C + +#新增自动助力活动格式 +# autoHelp 关键词 日志路径 变量名 + +############# 短期活动 ############# + + +############# 长期活动 ############# + +#东东农场 +autoHelp "东东农场好友互助码" "${logDir}/jd_fruit.log" "FRUITSHARECODES" + +#东东萌宠 +autoHelp "东东萌宠好友互助码" "${logDir}/jd_pet.log" "PETSHARECODES" + +#种豆得豆 +autoHelp "京东种豆得豆好友互助码" "${logDir}/jd_plantBean.log" "PLANT_BEAN_SHARECODES" + +#京喜工厂 +autoHelp "京喜工厂好友互助码" "${logDir}/jd_dreamFactory.log" "DREAM_FACTORY_SHARE_CODES" + +#东东工厂 +autoHelp "东东工厂好友互助码" "${logDir}/jd_jdfactory.log" "DDFACTORY_SHARECODES" + +#crazyJoy +autoHelp "crazyJoy任务好友互助码" "${logDir}/jd_crazy_joy.log" "JDJOY_SHARECODES" + +#京喜财富岛 +autoHelp "京喜财富岛好友互助码" "${logDir}/jd_cfd.log" "JDCFD_SHARECODES" + +#京喜农场 +autoHelp "京喜农场好友互助码" "${logDir}/jd_jxnc.log" "JXNC_SHARECODES" + +#京东赚赚 +autoHelp "京东赚赚好友互助码" "${logDir}/jd_jdzz.log" "JDZZ_SHARECODES" + +######### 日志打印格式需调整 ######### + +#口袋书店 +autoHelp "口袋书店好友互助码" "${logDir}/jd_bookshop.log" "BOOKSHOP_SHARECODES" + +#领现金 +autoHelp "签到领现金好友互助码" "${logDir}/jd_cash.log" "JD_CASH_SHARECODES" + +#闪购盲盒 +autoHelp "闪购盲盒好友互助码" "${logDir}/jd_sgmh.log" "JDSGMH_SHARECODES" + +#东东健康社区 +autoHelp "东东健康社区好友互助码" "${logDir}/jd_health.log" "JDHEALTH_SHARECODES" diff --git a/docker/bot/jd.png b/docker/bot/jd.png new file mode 100644 index 0000000000000000000000000000000000000000..c948dadaf6c083aced3892b33b992f835d578e9d GIT binary patch literal 7335 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 N002ovPDHLkV1iZA13CZz literal 0 HcmV?d00001 diff --git a/docker/bot/jd_bot b/docker/bot/jd_bot new file mode 100644 index 0000000..9b818f1 --- /dev/null +++ b/docker/bot/jd_bot @@ -0,0 +1,1114 @@ +# -*- coding: utf-8 -*- +# @Author : iouAkira(lof) +# @mail : e.akimoto.akira@gmail.com +# @CreateTime: 2020-11-02 +# @UpdateTime: 2021-03-23 + +import math +import os +import subprocess +from subprocess import TimeoutExpired +from MyQR import myqr +import requests +import time +import logging +import re +from urllib.parse import quote, unquote + +import telegram.utils.helpers as helpers +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ParseMode +from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackQueryHandler + +# 启用日志 +logging.basicConfig(format='%(asctime)s-%(name)s-%(levelname)s=> [%(funcName)s] %(message)s ', level=logging.INFO) +logger = logging.getLogger(__name__) + +_base_dir = '/scripts/' +_logs_dir = '%slogs/' % _base_dir +_docker_dir = '%sdocker/' % _base_dir +_bot_dir = '%sbot/' % _docker_dir +_share_code_conf = '%scode_gen_conf.list' % _logs_dir + +if 'GEN_CODE_CONF' in os.environ: + share_code_conf = os.getenv("GEN_CODE_CONF") + if share_code_conf.startswith("/"): + _share_code_conf = share_code_conf + + +def start(update, context): + from_user_id = update.message.from_user.id + if admin_id == str(from_user_id): + spnode_readme = "" + if "DISABLE_SPNODE" not in os.environ: + spnode_readme = "/spnode 获取可执行脚本的列表,选择对应的按钮执行。(拓展使用:运行指定路径脚本,例:/spnode /scripts/jd_818.js)\n\n" \ + "使用bot交互+spnode后 后续用户的cookie维护更新只需要更新logs/cookies.list即可\n" \ + "使用bot交互+spnode后 后续执行脚本命令请使用spnode否者无法使用logs/cookies.list的cookies执行脚本,定时任务也将自动替换为spnode命令执行\n" \ + "spnode功能概述示例\n\n" \ + "spnode conc /scripts/jd_bean_change.js 为每个cookie单独执行jd_bean_change脚本(伪并发\n" \ + "spnode 1 /scripts/jd_bean_change.js 为logs/cookies.list文件里面第一行cookie账户单独执行jd_bean_change脚本\n" \ + "spnode jd_XXXX /scripts/jd_bean_change.js 为logs/cookies.list文件里面pt_pin=jd_XXXX的cookie账户单独执行jd_bean_change脚本\n" \ + "spnode /scripts/jd_bean_change.js 为logs/cookies.list所有cookies账户一起执行jd_bean_change脚本\n" \ + "请仔细阅读并理解上面的内容,使用bot交互默认开启spnode指令功能功能。\n" \ + "如需____停用___请配置环境变量 -DISABLE_SPNODE=True" + context.bot.send_message(chat_id=update.effective_chat.id, + text="限制自己使用的JD Scripts拓展机器人\n" \ + "\n" \ + "/start 开始并获取指令说明\n" \ + "/node 获取可执行脚本的列表,选择对应的按钮执行。(拓展使用:运行指定路径脚本,例:/node /scripts/jd_818.js) \n" \ + "/git 获取可执行git指令列表,选择对应的按钮执行。(拓展使用:运行指定路径脚本,例:/git -C /scripts/ pull)\n" \ + "/logs 获取logs下的日志文件列表,选择对应名字可以下载日志文件\n" \ + "/env 获取系统环境变量列表。(拓展使用:设置系统环境变量,例:/env export JD_DEBUG=true,环境变量只针对当前bot进程生效) \n" \ + "/cmd 执行执行命令。参考:/cmd ls -l 涉及目录文件操作请使用绝对路径,部分shell命令开放使用\n" \ + "/gen_long_code 长期活动互助码提交消息生成\n" \ + "/gen_temp_code 短期临时活动互助码提交消息生成\n" \ + "/gen_daily_code 每天变化互助码活动提交消息生成\n\n%s" % spnode_readme) + else: + update.message.reply_text(text='此为私人使用bot,不能执行您的指令!') + + +def node(update, context): + """关于定时任务中nodejs脚本的相关操作 + """ + if is_admin(update.message.from_user.id): + commands = update.message.text.split() + commands.remove('/node') + if len(commands) > 0: + cmd = 'node %s' % (' '.join(commands)) + try: + + out_bytes = subprocess.check_output( + cmd, shell=True, timeout=3600, stderr=subprocess.STDOUT) + + out_text = out_bytes.decode('utf-8') + + if len(out_text.split()) > 50: + + msg = context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果超长,请查看log ↓↓↓' % cmd)), chat_id=update.effective_chat.id, + parse_mode=ParseMode.MARKDOWN_V2) + + log_name = '%sbot_%s_%s.log' % ( + _logs_dir, 'node', os.path.splitext(commands[-1])[0]) + + with open(log_name, 'a+') as wf: + wf.write(out_text) + + msg.reply_document( + reply_to_message_id=msg.message_id, quote=True, document=open(log_name, 'rb')) + else: + + context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果 ↓↓↓ \n\n%s ' % (cmd, out_text))), + chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + + except TimeoutExpired: + + context.bot.sendMessage(text='```{}```'.format(helpers.escape_markdown(' →→→ %s 执行超时 ←←← ' % ( + cmd))), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + + except: + + context.bot.sendMessage( + text='```{}```'.format(helpers.escape_markdown(' →→→ %s 执行出错,请检查确认命令是否正确 ←←← ' % ( + cmd))), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + raise + else: + reply_markup = get_reply_markup_btn('node') + update.message.reply_text(text='```{}```'.format(helpers.escape_markdown(' ↓↓↓ 请选择想要执行的nodejs脚本 ↓↓↓ ')), + reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2) + else: + update.message.reply_text(text='此为私人使用bot,不能执行您的指令!') + + +def spnode(update, context): + """关于定时任务中nodejs脚本的相关操作 + """ + if is_admin(update.message.from_user.id): + commands = update.message.text.split() + commands.remove('/spnode') + if len(commands) > 0: + cmd = 'spnode %s' % (' '.join(commands)) + try: + + out_bytes = subprocess.check_output( + cmd, shell=True, timeout=600, stderr=subprocess.STDOUT) + + out_text = out_bytes.decode('utf-8') + + if len(out_text.split()) > 50: + + msg = context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果超长,请查看log ↓↓↓' % cmd)), chat_id=update.effective_chat.id, + parse_mode=ParseMode.MARKDOWN_V2) + file_name = re.split(r"\W+", cmd) + if 'js' in file_name: + file_name.remove('js') + log_name = '%sbot_%s_%s.log' % (_logs_dir, 'spnode', file_name[-1]) + + with open(log_name, 'a+') as wf: + wf.write(out_text) + + msg.reply_document( + reply_to_message_id=msg.message_id, quote=True, document=open(log_name, 'rb')) + else: + + context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果 ↓↓↓ \n\n%s ' % (cmd, out_text))), + chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + + except TimeoutExpired: + + context.bot.sendMessage(text='```{}```'.format(helpers.escape_markdown(' →→→ %s 执行超时 ←←← ' % ( + cmd))), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + + except: + + context.bot.sendMessage( + text='```{}```'.format(helpers.escape_markdown(' →→→ %s 执行出错,请检查确认命令是否正确 ←←← ' % ( + cmd))), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + raise + else: + reply_markup = get_reply_markup_btn('spnode') + update.message.reply_text(text='```{}```'.format(helpers.escape_markdown(' ↓↓↓ 请选择想要执行的nodejs脚本 ↓↓↓ ')), + reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2) + else: + update.message.reply_text(text='此为私人使用bot,不能执行您的指令!') + + +def git(update, context): + """关于/scripts仓库的git相关操作 + """ + if is_admin(update.message.from_user.id): + commands = update.message.text.split() + commands.remove('/git') + if len(commands) > 0: + cmd = 'git %s' % (' '.join(commands)) + try: + + out_bytes = subprocess.check_output( + cmd, shell=True, timeout=600, stderr=subprocess.STDOUT) + + out_text = out_bytes.decode('utf-8') + + if len(out_text.split()) > 50: + + msg = context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果超长,请查看log ↓↓↓' % cmd)), chat_id=update.effective_chat.id, + parse_mode=ParseMode.MARKDOWN_V2) + + log_name = '%sbot_%s_%s.log' % ( + _logs_dir, 'git', os.path.splitext(commands[-1])[0]) + + with open(log_name, 'a+') as wf: + wf.write(out_text) + + msg.reply_document( + reply_to_message_id=msg.message_id, quote=True, document=open(log_name, 'rb')) + else: + + context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果 ↓↓↓ \n\n%s ' % (cmd, out_text))), + chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + + except TimeoutExpired: + + context.bot.sendMessage(text='```{}```'.format(helpers.escape_markdown(' →→→ %s 执行超时 ←←← ' % ( + cmd))), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + + except: + + context.bot.sendMessage( + text='```{}```'.format(helpers.escape_markdown(' →→→ %s 执行出错,请检查确认命令是否正确 ←←← ' % ( + cmd))), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + raise + + else: + reply_markup = get_reply_markup_btn('git') + + update.message.reply_text(text='```{}```'.format(helpers.escape_markdown(' ↓↓↓ 请选择想要执行的git指令 ↓↓↓ ')), + reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2) + else: + update.message.reply_text(text='此为私人使用bot,不能执行您的指令!') + + +def env(update, context): + """env 环境变量相关操作 + """ + if is_admin(update.message.from_user.id): + commands = update.message.text.split() + commands.remove('/env') + if len(commands) == 2 and (commands[0]) == 'export': + + try: + envs = commands[1].split('=') + os.putenv(envs[0], envs[1]) + + context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ 环境变量设置成功 ↓↓↓ \n\n%s ' % ('='.join(envs)))), + chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + except: + context.bot.sendMessage( + text='```{}```'.format(helpers.escape_markdown(' →→→ %s 执行出错,请检查确认命令是否正确 ←←← ' % ( + ' '.join(commands)))), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + raise + + elif len(commands) == 0: + out_bytes = subprocess.check_output( + 'env', shell=True, timeout=600, stderr=subprocess.STDOUT) + + out_text = out_bytes.decode('utf-8') + + if len(out_text.split()) > 50: + + msg = context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果超长,请查看log ↓↓↓' % 'env')), chat_id=update.effective_chat.id, + parse_mode=ParseMode.MARKDOWN_V2) + + log_name = '%sbot_%s.log' % (_logs_dir, 'env') + + with open(log_name, 'a+') as wf: + wf.write(out_text + '\n\n') + + msg.reply_document( + reply_to_message_id=msg.message_id, quote=True, document=open(log_name, 'rb')) + else: + + context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ env 执行结果 ↓↓↓ \n\n%s ' % out_text)), + chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + else: + context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' →→→ env 指令不正确,请参考说明输入 ←←← ')), chat_id=update.effective_chat.id, + parse_mode=ParseMode.MARKDOWN_V2) + + else: + update.message.reply_text(text='此为私人使用bot,不能执行您的指令!') + + +def crontab(update, context): + """关于crontab 定时任务相关操作 + """ + reply_markup = get_reply_markup_btn('crontab_l') + + if is_admin(update.message.from_user.id): + try: + update.message.reply_text(text='```{}```'.format(helpers.escape_markdown(' ↓↓↓ 下面为定时任务列表,请选择需要的操作 ↓↓↓ ')), + reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2) + except: + raise + else: + update.message.reply_text(text='此为私人使用bot,不能执行您的指令!') + + +def logs(update, context): + """关于_logs_dir目录日志文件的相关操作 + """ + reply_markup = get_reply_markup_btn('logs') + + if is_admin(update.message.from_user.id): + update.message.reply_text(text='```{}```'.format(helpers.escape_markdown(' ↓↓↓ 请选择想想要下载的日志文件或者清除所有日志 ↓↓↓ ')), + reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2) + else: + update.message.reply_text(text='此为私人使用bot,不能执行您的指令!') + + +def callback_run(update, context): + """执行按钮响应回调方法处理按钮点击事件 + """ + query = update.callback_query + select_btn_type = query.data.split()[0] + chat = query.message.chat + logger.info("callback query.message.chat ==> %s " % chat) + logger.info("callback query.data ==> %s " % query.data) + if select_btn_type == 'node' or select_btn_type == 'spnode' or select_btn_type == 'git': + try: + context.bot.edit_message_text(text='```{}```'.format(' ↓↓↓ 任务正在执行 ↓↓↓ \n\n%s' % query.data), + chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + out_bytes = subprocess.check_output( + query.data, shell=True, timeout=600, stderr=subprocess.STDOUT) + out_text = out_bytes.decode('utf-8') + if len(out_text.split()) > 50: + context.bot.edit_message_text(text='```{}```'.format(' ↓↓↓ %s 执行结果超长,请查看log ↓↓↓' % query.data), + chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + log_name = '%sbot_%s_%s.log' % ( + _logs_dir, select_btn_type, os.path.splitext(query.data.split('/')[-1])[0]) + logger.info('log_name==>' + log_name) + + with open(log_name, 'a+') as wf: + wf.write(out_text) + + query.message.reply_document( + reply_to_message_id=query.message.message_id, quote=True, document=open(log_name, 'rb')) + + else: + context.bot.edit_message_text(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果 ↓↓↓ \n\n%s ' % (query.data, out_text))), + chat_id=update.effective_chat.id, message_id=query.message.message_id, + parse_mode=ParseMode.MARKDOWN_V2) + except TimeoutExpired: + logger.error(' →→→ %s 执行超时 ←←← ' % query.data) + context.bot.edit_message_text(text='```{}```'.format(' →→→ %s 执行超时 ←←← ' % query.data), + chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + except: + context.bot.edit_message_text(text='```{}```'.format(' →→→ %s 执行出错 ←←← ' % query.data), + chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + raise + elif select_btn_type.startswith('cl'): + if select_btn_type == 'cl': + logger.info(f'crontab_l ==> {query.data.split()[1:]}') + reply_markup = InlineKeyboardMarkup([ + [InlineKeyboardButton( + ' '.join(query.data.split()[1:]), callback_data='pass')], + [InlineKeyboardButton('⚡️执行', callback_data='cle %s' % ' '.join(query.data.split()[1:])), + InlineKeyboardButton('❌删除', callback_data='cld %s' % ' '.join(query.data.split()[1:]))] + ]) + context.bot.edit_message_text(text='```{}```'.format(' ↓↓↓ 请选择对该定时任务的操作 ↓↓↓ '), + chat_id=query.message.chat_id, + reply_markup=reply_markup, message_id=query.message.message_id, + parse_mode=ParseMode.MARKDOWN_V2) + elif select_btn_type == 'cle': + cmd = ' '.join(query.data.split()[6:]) + try: + context.bot.edit_message_text(text='```{}```'.format(' ↓↓↓ 任务正在执行 ↓↓↓ \n\n%s' % cmd), + chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + out_bytes = subprocess.check_output(cmd, shell=True, timeout=600, + stderr=subprocess.STDOUT) + out_text = out_bytes.decode('utf-8') + if len(out_text.split()) > 50: + + context.bot.edit_message_text(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果超长,请查看log ↓↓↓' % cmd)), + chat_id=update.effective_chat.id, message_id=query.message.message_id, + parse_mode=ParseMode.MARKDOWN_V2) + + log_name = '%sbot_%s_%s.log' % ( + _logs_dir, select_btn_type, os.path.splitext(cmd.split('/')[-1])[0]) + + with open(log_name, 'a+') as wf: + wf.write(out_text) + + query.message.reply_document( + reply_to_message_id=query.message.message_id, quote=True, document=open(log_name, 'rb')) + + else: + context.bot.edit_message_text(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果 ↓↓↓ \n\n%s ' % (cmd, out_text))), + chat_id=update.effective_chat.id, message_id=query.message.message_id, + parse_mode=ParseMode.MARKDOWN_V2) + except TimeoutExpired: + logger.error(' →→→ %s 执行超时 ←←← ' % ' '.join(cmd[6:])) + context.bot.edit_message_text(text='```{}```'.format(' →→→ %s 执行超时 ←←← ' % cmd), + chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + except: + context.bot.edit_message_text(text='```{}```'.format(' →→→ %s 执行出错 ←←← ' % cmd), + chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + raise + elif select_btn_type == 'cld': + query.answer(text='删除任务功能暂未实现', show_alert=True) + + elif select_btn_type == 'logs': + log_file = query.data.split()[-1] + if log_file == 'clear': + cmd = 'rm -rf %s*.log' % _logs_dir + try: + subprocess.check_output( + cmd, shell=True, timeout=600, stderr=subprocess.STDOUT) + + context.bot.edit_message_text(text='```{}```'.format(' →→→ 日志文件已清除 ←←← '), + chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + except: + context.bot.sendMessage(text='```{}```'.format(helpers.escape_markdown( + ' →→→ 清除日志执行出错 ←←← ')), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + raise + else: + context.bot.edit_message_text(text='```{}```'.format(' ↓↓↓ 下面为下载的%s文件 ↓↓↓ ' % select_btn_type), + chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + + query.message.reply_document(reply_to_message_id=query.message.message_id, quote=True, + document=open(log_file, 'rb')) + + else: + context.bot.edit_message_text(text='```{}```'.format(' →→→ 操作已取消 ←←← '), chat_id=query.message.chat_id, + message_id=query.message.message_id, parse_mode=ParseMode.MARKDOWN_V2) + + +def get_reply_markup_btn(cmd_type): + """因为编辑后也可能会需要到重新读取按钮,所以抽出来作为一个方法返回按钮list + + Returns + ------- + 返回一个对应命令类型按钮列表,方便读取 + """ + if cmd_type == 'logs': + keyboard_line = [] + logs_list = get_dir_file_list(_logs_dir, 'log') + if (len(logs_list)) > 0: + file_list = list(set(get_dir_file_list(_logs_dir, 'log'))) + file_list.sort() + for i in range(math.ceil(len(file_list) / 2)): + ret = file_list[0:2] + keyboard_column = [] + for ii in ret: + keyboard_column.append(InlineKeyboardButton( + ii.split('/')[-1], callback_data='logs %s' % ii)) + file_list.remove(ii) + keyboard_line.append(keyboard_column) + + keyboard_line.append([InlineKeyboardButton('清除日志', callback_data='logs clear'), + InlineKeyboardButton('取消操作', callback_data='cancel')]) + else: + keyboard_line.append([InlineKeyboardButton( + '未找到相关日志文件', callback_data='cancel')]) + reply_markup = InlineKeyboardMarkup(keyboard_line) + elif cmd_type == 'crontab_l': + button_list = list(set(get_crontab_list(cmd_type))) + keyboard_line = [] + if len(button_list) > 0: + for i in button_list: + keyboard_column = [InlineKeyboardButton( + i, callback_data='cl %s' % i)] + + button_list.remove(i) + keyboard_line.append(keyboard_column) + keyboard_line.append( + [InlineKeyboardButton('取消操作', callback_data='cancel')]) + else: + keyboard_line.append([InlineKeyboardButton( + '未从%s获取到任务列表' % crontab_list_file, callback_data='cancel')]) + reply_markup = InlineKeyboardMarkup(keyboard_line) + elif cmd_type == 'node' or cmd_type == 'spnode': + button_list = list(set(get_crontab_list(cmd_type))) + button_list.sort() + keyboard_line = [] + for i in range(math.ceil(len(button_list) / 2)): + ret = button_list[0:2] + keyboard_column = [] + for ii in ret: + keyboard_column.append(InlineKeyboardButton( + ii.split('/')[-1], callback_data=ii)) + button_list.remove(ii) + keyboard_line.append(keyboard_column) + + keyboard_line.append( + [InlineKeyboardButton('取消操作', callback_data='cancel')]) + + reply_markup = InlineKeyboardMarkup(keyboard_line) + elif cmd_type == 'git': + # button_list = list(set(get_crontab_list(cmd_type))) + # button_list.sort() + keyboard_line = [[InlineKeyboardButton('git pull', callback_data='git -C %s pull' % _base_dir), + InlineKeyboardButton('git reset --hard', callback_data='git -C %s reset --hard' % _base_dir)], + [InlineKeyboardButton('取消操作', callback_data='cancel')]] + # for i in range(math.ceil(len(button_list) / 2)): + # ret = button_list[0:2] + # keyboard_column = [] + # for ii in ret: + # keyboard_column.append(InlineKeyboardButton( + # ii.split('/')[-1], callback_data=ii)) + # button_list.remove(ii) + # keyboard_line.append(keyboard_column) + # if len(keyboard_line) < 1: + # keyboard_line.append( + # [InlineKeyboardButton('git pull', callback_data='git -C %s pull' % _base_dir), + # InlineKeyboardButton('git reset --hard', callback_data='git -C %s reset --hard' % _base_dir)]) + reply_markup = InlineKeyboardMarkup(keyboard_line) + else: + reply_markup = InlineKeyboardMarkup( + [[InlineKeyboardButton('没找到对的命令操作按钮', callback_data='cancel')]]) + + return reply_markup + + +def get_crontab_list(cmd_type): + """获取任务列表里面的node相关的定时任务 + + Parameters + ---------- + cmd_type: 是任务的命令类型 + # item_idx: 确定取的需要取之是定任务里面第几个空格后的值作为后面执行指令参数 + + Returns + ------- + 返回一个指定命令类型定时任务列表 + """ + button_list = [] + try: + with open(_docker_dir + crontab_list_file) as lines: + array = lines.readlines() + for i in array: + i = i.replace('|ts', '').strip('\n') + if i.startswith('#') or len(i) < 5: + pass + else: + items = i.split('>>') + item_sub = items[0].split()[5:] + # logger.info(item_sub[0]) + if cmd_type.find(item_sub[0]) > -1: + if cmd_type == 'spnode': + # logger.info(str(' '.join(item_sub)).replace('node','spnode')) + button_list.append(str(' '.join(item_sub)).replace('node', 'spnode')) + else: + button_list.append(' '.join(item_sub)) + elif cmd_type == 'crontab_l': + button_list.append(items[0]) + except: + logger.warning(f'读取定时任务配置文件 {crontab_list_file} 出错') + finally: + return button_list + + +def get_dir_file_list(dir_path, file_type): + """获取传入的路径下的文件列表 + + Parameters + ---------- + dir_path: 完整的目录路径,不需要最后一个 + file_type: 或者文件的后缀名字 + + Returns + ------- + 返回一个完整绝对路径的文件列表,方便读取 + """ + cmd = 'ls %s*.%s' % (dir_path, file_type) + file_list = [] + try: + out_bytes = subprocess.check_output( + cmd, shell=True, timeout=5, stderr=subprocess.STDOUT) + out_text = out_bytes.decode('utf-8') + file_list = out_text.split() + except: + logger.warning(f'{dir_path}目录下,不存在{file_type}对应的文件') + finally: + return file_list + + +def is_admin(from_user_id): + if str(admin_id) == str(from_user_id): + return True + else: + return False + + +class CodeConf(object): + def __init__(self, bot_id, submit_code, log_name, activity_code, find_split_char): + self.bot_id = bot_id + self.submit_code = submit_code + self.log_name = log_name + self.activity_code = activity_code + self.find_split_char = find_split_char + + def get_submit_msg(self): + code_list = [] + ac = self.activity_code if self.activity_code != "@N" else "" + try: + with open("%s%s" % (_logs_dir, self.log_name), 'r') as lines: + array = lines.readlines() + for i in array: + # print(self.find_split_char) + if i.find(self.find_split_char) > -1: + code_list.append(i.split(self.find_split_char)[ + 1].replace('\n', '')) + if self.activity_code == "@N": + return '%s %s' % (self.submit_code, + "&".join(list(set(code_list)))) + else: + return '%s %s %s' % (self.submit_code, ac, + "&".join(list(set(code_list)))) + except: + return "%s %s活动获取系统日志文件异常,请检查日志文件是否存在" % (self.submit_code, ac) + + +def gen_long_code(update, context): + """ + 长期活动互助码提交消息生成 + """ + long_code_conf = [] + bot_list = [] + try: + with open(_share_code_conf, 'r') as lines: + array = lines.readlines() + for i in array: + if i.startswith("long"): + bot_list.append(i.split('-')[1]) + code_conf = CodeConf( + i.split('-')[1], i.split('-')[2], i.split('-')[3], i.split('-')[4], + i.split('-')[5].replace('\n', '')) + long_code_conf.append(code_conf) + + for bot in list(set(bot_list)): + for cf in long_code_conf: + if cf.bot_id == bot: + print() + context.bot.send_message(chat_id=update.effective_chat.id, text=cf.get_submit_msg()) + context.bot.send_message(chat_id=update.effective_chat.id, text="以上为 %s 可以提交的活动互助码" % bot) + except: + context.bot.send_message(chat_id=update.effective_chat.id, + text="获取互助码消息生成配置文件失败,请检查%s文件是否存在" % _share_code_conf) + + +def gen_temp_code(update, context): + """ + 短期临时活动互助码提交消息生成 + """ + temp_code_conf = [] + bot_list = [] + try: + with open(_share_code_conf, 'r') as lines: + array = lines.readlines() + for i in array: + if i.startswith("temp"): + bot_list.append(i.split('-')[1]) + code_conf = CodeConf( + i.split('-')[1], i.split('-')[2], i.split('-')[3], i.split('-')[4], + i.split('-')[5].replace('\n', '')) + temp_code_conf.append(code_conf) + + for bot in list(set(bot_list)): + for cf in temp_code_conf: + if cf.bot_id == bot: + print() + context.bot.send_message(chat_id=update.effective_chat.id, text=cf.get_submit_msg()) + context.bot.send_message(chat_id=update.effective_chat.id, text="以上为 %s 可以提交的短期临时活动互助码" % bot) + except: + context.bot.send_message(chat_id=update.effective_chat.id, + text="获取互助码消息生成配置文件失败,请检查%s文件是否存在" % _share_code_conf) + + +def gen_daily_code(update, context): + """ + 每天变化互助码活动提交消息生成 + """ + daily_code_conf = [] + bot_list = [] + try: + with open(_share_code_conf, 'r') as lines: + array = lines.readlines() + for i in array: + if i.startswith("daily"): + bot_list.append(i.split('-')[1]) + code_conf = CodeConf( + i.split('-')[1], i.split('-')[2], i.split('-')[3], i.split('-')[4], + i.split('-')[5].replace('\n', '')) + daily_code_conf.append(code_conf) + + for bot in list(set(bot_list)): + for cf in daily_code_conf: + if cf.bot_id == bot: + print() + context.bot.send_message(chat_id=update.effective_chat.id, text=cf.get_submit_msg()) + context.bot.send_message(chat_id=update.effective_chat.id, text="以上为 %s 可以提交的每天变化活动互助码" % bot) + except: + context.bot.send_message(chat_id=update.effective_chat.id, + text="获取互助码消息生成配置文件失败,请检查%s文件是否存在" % _share_code_conf) + + +def shcmd(update, context): + """ + 执行终端命令,超时时间为60,执行耗时或者不退出的指令会报超时异常 + """ + if is_admin(update.message.from_user.id): + commands = update.message.text.split() + commands.remove('/cmd') + if len(commands) > 0: + support_cmd = ["echo", "ls", "pwd", "cp", "mv", "ps", "wget", "cat", "sed", "git", "apk", "sh", + "docker_entrypoint.sh"] + if commands[0] in support_cmd: + sp_cmd = ["sh", "docker_entrypoint.sh"] + cmd = ' '.join(commands) + try: + # 测试发现 subprocess.check_output 执行shell 脚本文件的时候无法正常执行获取返回结果 + # 所以 ["sh", "docker_entrypoint.sh"] 指令换为 subprocess.Popen 方法执行 + out_text = "" + if commands[0] in sp_cmd: + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + while p.poll() is None: + line = p.stdout.readline() + # logger.info(line.decode('utf-8')) + out_text = out_text + line.decode('utf-8') + else: + out_bytes = subprocess.check_output( + cmd, shell=True, timeout=60, stderr=subprocess.STDOUT) + out_text = out_bytes.decode('utf-8') + + if len(out_text.split('\n')) > 50: + msg = context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果超长,请查看log ↓↓↓' % cmd)), + chat_id=update.effective_chat.id, + parse_mode=ParseMode.MARKDOWN_V2) + log_name = '%sbot_%s_%s.log' % (_logs_dir, 'cmd', commands[0]) + with open(log_name, 'w') as wf: + wf.write(out_text) + msg.reply_document( + reply_to_message_id=msg.message_id, quote=True, document=open(log_name, 'rb')) + else: + context.bot.sendMessage(text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 执行结果 ↓↓↓ \n\n%s ' % (cmd, out_text))), + chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + + except TimeoutExpired: + context.bot.sendMessage(text='```{}```'.format(helpers.escape_markdown(' →→→ %s 执行超时 ←←← ' % ( + cmd))), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + except Exception as e: + context.bot.sendMessage( + text='```{}```'.format(helpers.escape_markdown(' →→→ %s 执行出错,请检查确认命令是否正确 ←←← ' % ( + cmd))), chat_id=update.effective_chat.id, parse_mode=ParseMode.MARKDOWN_V2) + logger.error(e) + else: + update.message.reply_text( + text='```{}```'.format( + helpers.escape_markdown( + f' →→→ {commands[0]}指令不在支持命令范围,请输入其他支持的指令{"|".join(support_cmd)} ←←← ')), + parse_mode=ParseMode.MARKDOWN_V2) + else: + update.message.reply_text( + text='```{}```'.format(helpers.escape_markdown(' →→→ 请在/cmd 后写自己需要执行的指的命令 ←←← ')), + parse_mode=ParseMode.MARKDOWN_V2) + else: + update.message.reply_text(text='此为私人使用bot,不能执行您的指令!') + + +# getSToken请求获取,s_token用于发送post请求是的必须参数 +s_token = "" +# getSToken请求获取,guid,lsid,lstoken用于组装cookies +guid, lsid, lstoken = "", "", "" +# 由上面参数组装生成,getOKLToken函数发送请求需要使用 +cookies = "" +# getOKLToken请求获取,token用户生成二维码使用、okl_token用户检查扫码登录结果使用 +token, okl_token = "", "" +# 最终获取到的可用的cookie +jd_cookie = "" + + +def get_jd_cookie(update, context): + getSToken() + getOKLToken() + + qr_code_path = genQRCode() + photo_file = open(qr_code_path, 'rb') + photo_message = context.bot.send_photo(chat_id=update.effective_chat.id, photo=photo_file, + caption="请使用京东APP扫描二维码获取Cookie(二维码有效期:3分钟)") + photo_file.close() + + return_msg = chekLogin() + if return_msg == 0: + context.bot.delete_message(chat_id=update.effective_chat.id, message_id=photo_message.message_id) + context.bot.send_message(chat_id=update.effective_chat.id, text="获取Cookie成功\n`%s`" % jd_cookie, + parse_mode=ParseMode.MARKDOWN_V2) + + elif return_msg == 21: + context.bot.delete_message(chat_id=update.effective_chat.id, message_id=photo_message.message_id) + context.bot.edit_message_text(chat_id=update.effective_chat.id, message_id=photo_message.message_id, + text="二维码已经失效,请重新获取") + else: + context.bot.delete_message(chat_id=update.effective_chat.id, message_id=photo_message.message_id) + context.bot.edit_message_text(chat_id=update.effective_chat.id, message_id=photo_message.message_id, + text=return_msg) + + +def getSToken(): + time_stamp = int(time.time() * 1000) + get_url = 'https://plogin.m.jd.com/cgi-bin/mm/new_login_entrance?lang=chs&appid=300&returnurl=https://wq.jd.com/passport/LoginRedirect?state=%s&returnurl=https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport' % time_stamp + get_header = { + 'Connection': 'Keep-Alive', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-cn', + 'Referer': 'https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wq.jd.com/passport/LoginRedirect?state=%s&returnurl=https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport' % time_stamp, + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36', + 'Host': 'plogin.m.jd.com' + } + try: + resp = requests.get(url=get_url, headers=get_header) + parseGetRespCookie(resp.headers, resp.json()) + logger.info(resp.headers) + logger.info(resp.json()) + except Exception as error: + logger.exception("Get网络请求异常", error) + + +def parseGetRespCookie(headers, get_resp): + global s_token + global cookies + s_token = get_resp.get('s_token') + set_cookies = headers.get('set-cookie') + logger.info(set_cookies) + + guid = re.findall(r"guid=(.+?);", set_cookies)[0] + lsid = re.findall(r"lsid=(.+?);", set_cookies)[0] + lstoken = re.findall(r"lstoken=(.+?);", set_cookies)[0] + + cookies = f"guid={guid}; lang=chs; lsid={lsid}; lstoken={lstoken}; " + logger.info(cookies) + + +def getOKLToken(): + post_time_stamp = int(time.time() * 1000) + post_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthreflogurl?s_token=%s&v=%s&remember=true' % ( + s_token, post_time_stamp) + post_data = { + 'lang': 'chs', + 'appid': 300, + 'returnurl': 'https://wqlogin2.jd.com/passport/LoginRedirect?state=%s&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action' % post_time_stamp, + 'source': 'wq_passport' + } + post_header = { + 'Connection': 'Keep-Alive', + 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', + 'Accept': 'application/json, text/plain, */*', + 'Cookie': cookies, + 'Referer': 'https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=%s&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport' % post_time_stamp, + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36', + 'Host': 'plogin.m.jd.com', + } + try: + global okl_token + resp = requests.post(url=post_url, headers=post_header, data=post_data, timeout=20) + parsePostRespCookie(resp.headers, resp.json()) + logger.info(resp.headers) + except Exception as error: + logger.exception("Post网络请求错误", error) + + +def parsePostRespCookie(headers, data): + global token + global okl_token + + token = data.get('token') + print(headers.get('set-cookie')) + okl_token = re.findall(r"okl_token=(.+?);", headers.get('set-cookie'))[0] + + logger.info("token:" + token) + logger.info("okl_token:" + okl_token) + + +def genQRCode(): + global qr_code_path + cookie_url = f'https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token=%s' % token + version, level, qr_name = myqr.run( + words=cookie_url, + # 扫描二维码后,显示的内容,或是跳转的链接 + version=5, # 设置容错率 + level='H', # 控制纠错水平,范围是L、M、Q、H,从左到右依次升高 + picture='/scripts/docker/bot/jd.png', # 图片所在目录,可以是动图 + colorized=True, # 黑白(False)还是彩色(True) + contrast=1.0, # 用以调节图片的对比度,1.0 表示原始图片。默认为1.0。 + brightness=1.0, # 用来调节图片的亮度,用法同上。 + save_name='/scripts/docker/genQRCode.png', # 控制输出文件名,格式可以是 .jpg, .png ,.bmp ,.gif + ) + return qr_name + + +def chekLogin(): + expired_time = time.time() + 60 * 3 + while True: + check_time_stamp = int(time.time() * 1000) + check_url = 'https://plogin.m.jd.com/cgi-bin/m/tmauthchecktoken?&token=%s&ou_state=0&okl_token=%s' % ( + token, okl_token) + check_data = { + 'lang': 'chs', + 'appid': 300, + 'returnurl': 'https://wqlogin2.jd.com/passport/LoginRedirect?state=%s&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action' % check_time_stamp, + 'source': 'wq_passport' + + } + check_header = { + 'Referer': f'https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=%s&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport' % check_time_stamp, + 'Cookie': cookies, + 'Connection': 'Keep-Alive', + 'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8', + 'Accept': 'application/json, text/plain, */*', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36', + + } + resp = requests.post(url=check_url, headers=check_header, data=check_data, timeout=30) + data = resp.json() + if data.get("errcode") == 0: + parseJDCookies(resp.headers) + return data.get("errcode") + if data.get("errcode") == 21: + return data.get("errcode") + if time.time() > expired_time: + return "超过3分钟未扫码,二维码已过期。" + + +def parseJDCookies(headers): + global jd_cookie + logger.info("扫码登录成功,下面为获取到的用户Cookie。") + set_cookie = headers.get('Set-Cookie') + pt_key = re.findall(r"pt_key=(.+?);", set_cookie)[0] + pt_pin = re.findall(r"pt_pin=(.+?);", set_cookie)[0] + logger.info(pt_key) + logger.info(pt_pin) + jd_cookie = f'pt_key={pt_key};pt_pin={pt_pin};' + + +def saveFile(update, context): + from_user_id = update.message.from_user.id + if admin_id == str(from_user_id): + js_file_name = update.message.document.file_name + if str(js_file_name).endswith(".js"): + save_path = f"{_base_dir}{js_file_name}" + try: + # logger.info(update.message) + file = context.bot.getFile(update.message.document.file_id) + file.download(save_path) + keyboard_line = [[InlineKeyboardButton('node 执行', callback_data='node %s ' % save_path), + InlineKeyboardButton('spnode 执行', + callback_data='spnode %s' % save_path)], + [InlineKeyboardButton('取消操作', callback_data='cancel')]] + + reply_markup = InlineKeyboardMarkup(keyboard_line) + + update.message.reply_text( + text='```{}```'.format( + helpers.escape_markdown(' ↓↓↓ %s 上传至/scripts完成,请请选择需要的操作 ↓↓↓ ' % js_file_name)), + reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN_V2) + + except Exception as e: + update.message.reply_text(text='```{}```'.format( + helpers.escape_markdown(" →→→ %s js上传至/scripts过程中出错,请重新尝试。 ←←← " % js_file_name)), + parse_mode=ParseMode.MARKDOWN_V2) + else: + update.message.reply_text(text='```{}```'.format( + helpers.escape_markdown(" →→→ 抱歉,暂时只开放上传js文件至/scripts目录 ←←← ")), + parse_mode=ParseMode.MARKDOWN_V2) + + +def unknown(update, context): + """回复用户输入不存在的指令 + """ + from_user_id = update.message.from_user.id + if admin_id == str(from_user_id): + spnode_readme = "" + if "DISABLE_SPNODE" not in os.environ: + spnode_readme = "/spnode 获取可执行脚本的列表,选择对应的按钮执行。(拓展使用:运行指定路径脚本,例:/spnode /scripts/jd_818.js)\n\n" \ + "使用bot交互+spnode后 后续用户的cookie维护更新只需要更新logs/cookies.list即可\n" \ + "使用bot交互+spnode后 后续执行脚本命令请使用spnode否者无法使用logs/cookies.list的cookies执行脚本,定时任务也将自动替换为spnode命令执行\n" \ + "spnode功能概述示例\n\n" \ + "spnode conc /scripts/jd_bean_change.js 为每个cookie单独执行jd_bean_change脚本(伪并发\n" \ + "spnode 1 /scripts/jd_bean_change.js 为logs/cookies.list文件里面第一行cookie账户单独执行jd_bean_change脚本\n" \ + "spnode jd_XXXX /scripts/jd_bean_change.js 为logs/cookies.list文件里面pt_pin=jd_XXXX的cookie账户单独执行jd_bean_change脚本\n" \ + "spnode /scripts/jd_bean_change.js 为logs/cookies.list所有cookies账户一起执行jd_bean_change脚本\n" \ + "请仔细阅读并理解上面的内容,使用bot交互默认开启spnode指令功能功能。\n" \ + "如需____停用___请配置环境变量 -DISABLE_SPNODE=True" + update.message.reply_text(text="⚠️ 您输入了一个错误的指令,请参考说明使用\n" \ + "\n" \ + "/start 开始并获取指令说明\n" \ + "/node 获取可执行脚本的列表,选择对应的按钮执行。(拓展使用:运行指定路径脚本,例:/node /scripts/jd_818.js) \n" \ + "/git 获取可执行git指令列表,选择对应的按钮执行。(拓展使用:运行指定路径脚本,例:/git -C /scripts/ pull)\n" \ + "/logs 获取logs下的日志文件列表,选择对应名字可以下载日志文件\n" \ + "/env 获取系统环境变量列表。(拓展使用:设置系统环境变量,例:/env export JD_DEBUG=true,环境变量只针对当前bot进程生效) \n" \ + "/cmd 执行执行命令。参考:/cmd ls -l 涉及目录文件操作请使用绝对路径,部分shell命令开放使用\n" \ + "/gen_long_code 长期活动互助码提交消息生成\n" \ + "/gen_temp_code 短期临时活动互助码提交消息生成\n" \ + "/gen_daily_code 每天变化互助码活动提交消息生成\n\n%s" % spnode_readme, + parse_mode=ParseMode.HTML) + else: + update.message.reply_text(text='此为私人使用bot,不能执行您的指令!') + + +def error(update, context): + """Log Errors caused by Updates.""" + logger.warning('Update "%s" caused error "%s"', update, context.error) + context.bot.send_message( + 'Update "%s" caused error "%s"', update, context.error) + + +def main(): + global admin_id, bot_token, crontab_list_file + + if 'TG_BOT_TOKEN' in os.environ: + bot_token = os.getenv('TG_BOT_TOKEN') + + if 'TG_USER_ID' in os.environ: + admin_id = os.getenv('TG_USER_ID') + + if 'CRONTAB_LIST_FILE' in os.environ: + crontab_list_file = os.getenv('CRONTAB_LIST_FILE') + else: + crontab_list_file = 'crontab_list.sh' + + logger.info('CRONTAB_LIST_FILE=' + crontab_list_file) + + # 创建更新程序并参数为你Bot的TOKEN。 + updater = Updater(bot_token, use_context=True) + + # 获取调度程序来注册处理程序 + dp = updater.dispatcher + + # 通过 start 函数 响应 '/start' 命令 + dp.add_handler(CommandHandler('start', start)) + + # 通过 start 函数 响应 '/help' 命令 + dp.add_handler(CommandHandler('help', start)) + + # 通过 node 函数 响应 '/node' 命令 + dp.add_handler(CommandHandler('node', node)) + + # 通过 node 函数 响应 '/spnode' 命令 + dp.add_handler(CommandHandler('spnode', spnode)) + + # 通过 git 函数 响应 '/git' 命令 + dp.add_handler(CommandHandler('git', git)) + + # 通过 crontab 函数 响应 '/crontab' 命令 + dp.add_handler(CommandHandler('crontab', crontab)) + + # 通过 logs 函数 响应 '/logs' 命令 + dp.add_handler(CommandHandler('logs', logs)) + + # 通过 cmd 函数 响应 '/cmd' 命令 + dp.add_handler(CommandHandler('cmd', shcmd)) + + # 通过 callback_run 函数 响应相关按钮命令 + dp.add_handler(CallbackQueryHandler(callback_run)) + + # 通过 env 函数 响应 '/env' 命令 + dp.add_handler(CommandHandler('env', env)) + + # 通过 gen_long_code 函数 响应 '/gen_long_code' 命令 + dp.add_handler(CommandHandler('gen_long_code', gen_long_code)) + + # 通过 gen_temp_code 函数 响应 '/gen_temp_code' 命令 + dp.add_handler(CommandHandler('gen_temp_code', gen_temp_code)) + + # 通过 gen_daily_code 函数 响应 '/gen_daily_code' 命令 + dp.add_handler(CommandHandler('gen_daily_code', gen_daily_code)) + + # 通过 get_jd_cookie 函数 响应 '/eikooc_dj_teg' 命令 #别问为啥这么写,有意为之的 + dp.add_handler(CommandHandler('eikooc_dj_teg', get_jd_cookie)) + + # 文件监听 + dp.add_handler(MessageHandler(Filters.document, saveFile)) + + # 没找到对应指令 + dp.add_handler(MessageHandler(Filters.command, unknown)) + + # 响应普通文本消息 + # dp.add_handler(MessageHandler(Filters.text, resp_text)) + + dp.add_error_handler(error) + + updater.start_polling() + updater.idle() + + +# 生成依赖安装列表 +# pip3 freeze > requirements.txt +# 或者使用pipreqs +# pip3 install pipreqs +# 在当前目录生成 +# pipreqs . --encoding=utf8 --force +# 使用requirements.txt安装依赖 +# pip3 install -r requirements.txt +if __name__ == '__main__': + main() diff --git a/docker/bot/requirements.txt b/docker/bot/requirements.txt new file mode 100644 index 0000000..4b29cbe --- /dev/null +++ b/docker/bot/requirements.txt @@ -0,0 +1,5 @@ +python_telegram_bot==13.0 +requests==2.23.0 +MyQR==2.3.1 +telegram==0.0.1 +tzlocal<3.0 diff --git a/docker/bot/setup.py b/docker/bot/setup.py new file mode 100644 index 0000000..a1be8e0 --- /dev/null +++ b/docker/bot/setup.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# @Author : iouAkira(lof) +# @mail : e.akimoto.akira@gmail.com +# @CreateTime: 2020-11-02 +# @UpdateTime: 2021-03-21 + +from setuptools import setup + +setup( + name='jd-scripts-bot', + version='0.2', + scripts=['jd_bot', ], +) diff --git a/docker/crontab_list.sh b/docker/crontab_list.sh new file mode 100644 index 0000000..c376967 --- /dev/null +++ b/docker/crontab_list.sh @@ -0,0 +1,101 @@ +# 每3天的23:50分清理一次日志(互助码不清理,proc_file.sh对该文件进行了去重) +50 23 */3 * * find /scripts/logs -name '*.log' | grep -v 'sharecodeCollection' | xargs rm -rf +#收集助力码 +30 * * * * sh +x /scripts/docker/auto_help.sh collect >> /scripts/logs/auto_help_collect.log 2>&1 + +##############活动############## + +# 京东资产变动 +30 21 * * * node /scripts/jd_bean_change_pro.js >> /scripts/logs/jd_bean_change_pro.log 2>&1 +# 京豆详情统计 +20 22 * * * node /scripts/jd_bean_info.js >> /scripts/logs/jd_bean_info.log 2>&1 +# 美丽研究院 +20 7,12,19 * * * node /scripts/jd_beauty.js >> /scripts/logs/jd_beauty.log 2>&1 +# 头文字J +30 4,16 * * * node /scripts/jd_car_play.js >> /scripts/logs/jd_car_play.log 2>&1 +# 头文字J兑换 +0 10 * * * node /scripts/jd_car_play_exchange.js >> /scripts/logs/jd_car_play_exchange.log 2>&1 +# 签到领现金 +30 8,14 * * * node /scripts/jd_cash.js >> /scripts/logs/jd_cash.log 2>&1 +# 清空购物车 +53 22 * * * node /scripts/jd_cleancart_nolan.js >> /scripts/logs/jd_cleancart_nolan.log 2>&1 +# 摇京豆 +11 0,18 * * * node /scripts/jd_club_lottery.js >> /scripts/logs/jd_club_lottery.log 2>&1 +# 东东乐园 +30 7 * * * node /scripts/jd_ddnc_farmpark.js >> /scripts/logs/jd_ddnc_farmpark.log 2>&1 +# 店铺签到 +15 2,14 * * * node /scripts/jd_dpqd.js >> /scripts/logs/jd_dpqd.log 2>&1 +# 临期京豆兑换 +0 0 * * * node /scripts/jd_exchange.js >> /scripts/logs/jd_exchange.log 2>&1 +# 签到 +0 3 * * * node /scripts/jd_jdgj_sign.js >> /scripts/logs/jd_jdgj_sign.log 2>&1 +# 天天领京豆兑换 +0 21 * * * node /scripts/jd_lzkj_ttljd_exchange.js >> /scripts/logs/jd_lzkj_ttljd_exchange.log 2>&1 +# 天天领京豆 +0 7 * * * node /scripts/jd_lzkj_ttljd.js >> /scripts/logs/jd_lzkj_ttljd.log 2>&1 +# 积分换话费 +33 7 * * * node /scripts/jd_dwapp.js >> /scripts/logs/jd_dwapp.log 2>&1 +# 东东农场好友删减奖励 +10 2 * * * node /scripts/jd_fruit_friend.js >> /scripts/logs/jd_fruit_friend.log 2>&1 +# 东东农场内部水滴互助 +20 4,16 * * * node /scripts/jd_fruit_help.js >> /scripts/logs/jd_fruit_help.log 2>&1 +# 东东农场日常任务 +5 6-18/6 * * * node /scripts/jd_fruit_task.js >> /scripts/logs/jd_fruit_task.log 2>&1 +# 京洞察问卷通知 +35 11 * * * node /scripts/jd_insight.js >> /scripts/logs/jd_insight.log 2>&1 +# 汪汪乐园每日任务 +0 1,7,20 * * * node /scripts/jd_joy_park_task.js >> /scripts/logs/jd_joy_park_task.log 2>&1 +# JOY庄园每日任务 +11 1,15 * * * node /scripts/jd_joymanor_task.js >> /scripts/logs/jd_joymanor_task.log 2>&1 +# 极速版签到提现-加密 +30 0,15 * * * node /scripts/jd_tj_sign.js >> /scripts/logs/jd_tj_signn.log 2>&1 +# 京东快递签到 +10 0 * * * node /scripts/jd_kd.js >> /scripts/logs/jd_kd.log 2>&1 +# 京东快递签到 +10 3 * * * node /scripts/jd_kuaidi_leaf.js >> /scripts/logs/jd_kuaidi_leaf.log 2>&1 +# 生鲜早起打卡 +15 6,7 * * * node /scripts/jd_morningSc.js >> /scripts/logs/jd_morningSc.log 2>&1 +# 种豆得豆 +1 7-21/2 * * * node /scripts/jd_plantBean.js >> /scripts/logs/jd_plantBean.log 2>&1 +# 种豆得豆内部互助 +40 4,17 * * * node /scripts/jd_plantBean_help.js >> /scripts/logs/jd_plantBean_help.log 2>&1 +# 京东保价 +39 20 * * * node /scripts/jd_price.js >> /scripts/logs/jd_price.log 2>&1 +# QQ星系牧场 +22 4-22/3 * * * node /scripts/jd_qqxing.js >> /scripts/logs/jd_qqxing.log 2>&1 +# 闪购签到有礼 +10 10 * * * node /scripts/jd_shangou.js >> /scripts/logs/jd_shangou.log 2>&1 +# 小豆签到 +48 2,19 * * * node /scripts/jd_krSign.js >> /scripts/logs/jd_krSign.log 2>&1 +# 小豆签到 +48 1,18 * * * node /scripts/jd_beanSign.js >> /scripts/logs/jd_beanSign.log 2>&1 +# 京东签到翻牌 +10 8 * * * node /scripts/jd_sign_graphics.js >> /scripts/logs/jd_sign_graphics.log 2>&1 +# 京东极速版领红包-加密 +20 0,22 * * * node /scripts/jd_speed_redpocke.js >> /scripts/logs/jd_speed_redpocke.log 2>&1 +# 京东极速版 +21 3,8 * * * node /scripts/jd_speed_sign.js >> /scripts/logs/jd_speed_sign.log 2>&1 +# 特务Z-II +35 10,18,20 * * * node /scripts/jd_superBrand.js >> /scripts/logs/jd_superBrand.log 2>&1 +# 特务集卡 +2 10,18,20 * * * node /scripts/jd_superBrandJK.js >> /scripts/logs/jd_superBrandJK.log 2>&1 +# 特务集勋章 +8 10,18,20 * * * node /scripts/jd_superBrandJXZ.js >> /scripts/logs/jd_superBrandJXZ.log 2>&1 +# 特务之明星送好礼 +36 2,19 * * * node /scripts/jd_superBrandStar.js >> /scripts/logs/jd_superBrandStar.log 2>&1 +# 京东试用 +44 1-22/5 * * * node /scripts/jd_try.js >> /scripts/logs/jd_try.log 2>&1 +# 京东试用待领取通知 +22 15 * * * node /scripts/jd_try_notify.js >> /scripts/logs/jd_try_notify.log 2>&1 +# 取关所有主播 +55 22 * * * node /scripts/jd_unsubscriLive.js >> /scripts/logs/jd_unsubscriLive.log 2>&1 +# 批量取关店铺和商品 +22 22 * * * node /scripts/jd_unsubscribe.js >> /scripts/logs/jd_unsubscribe.log 2>&1 +# 众筹许愿池 +40 0,2 * * * node /scripts/jd_wish.js >> /scripts/logs/jd_wish.log 2>&1 +# 京享周周乐 +2 6 * * 5 node /scripts/jd_xs_zzl.js >> /scripts/logs/jd_xs_zzl.log 2>&1 +# plus专属礼 +2 6 * * 5 node /scripts/jd_plus2bean.js >> /scripts/logs/jd_plus2bean.log 2>&1 +# 红包团 +40 1 * * * node /scripts/jd_wechat_openGroup.js >> /scripts/logs/jd_wechat_openGroup.log 2>&1 \ No newline at end of file diff --git a/docker/default_task.sh b/docker/default_task.sh new file mode 100644 index 0000000..7cc7e18 --- /dev/null +++ b/docker/default_task.sh @@ -0,0 +1,252 @@ +#!/bin/sh +set -e + +# 放在这个初始化python3环境,目的减小镜像体积,一些不需要使用bot交互的用户可以不用拉体积比较大的镜像 +# 在这个任务里面还有初始化还有目的就是为了方便bot更新了新功能的话只需要重启容器就完成更新 +function initPythonEnv() { + echo "开始安装运行jd_bot需要的python环境及依赖..." + apk add --update python3-dev py3-pip py3-cryptography py3-numpy py-pillow + echo "开始安装jd_bot依赖..." + #测试 + #cd /jd_docker/docker/bot + #合并 + cd /scripts/docker/bot + pip3 install --upgrade pip + pip3 install -r requirements.txt + python3 setup.py install +} + +#启动tg bot交互前置条件成立,开始安装配置环境 +if [ "$1" == "True" ]; then + initPythonEnv + if [ -z "$DISABLE_SPNODE" ]; then + echo "增加命令组合spnode ,使用该命令spnode jd_xxxx.js 执行js脚本会读取cookies.conf里面的jd cokie账号来执行脚本" + ( + cat </usr/local/bin/spnode + chmod +x /usr/local/bin/spnode + fi + + echo "spnode需要使用的到,cookie写入文件,该文件同时也为jd_bot扫码获自动取cookies服务" + if [ -z "$JD_COOKIE" ]; then + if [ ! -f "$COOKIES_LIST" ]; then + echo "" >"$COOKIES_LIST" + echo "未配置JD_COOKIE环境变量,$COOKIES_LIST文件已生成,请将cookies写入$COOKIES_LIST文件,格式每个Cookie一行" + fi + else + if [ -f "$COOKIES_LIST" ]; then + echo "cookies.conf文件已经存在跳过,如果需要更新cookie请修改$COOKIES_LIST文件内容" + else + echo "环境变量 cookies写入$COOKIES_LIST文件,如果需要更新cookie请修改cookies.conf文件内容" + echo $JD_COOKIE | sed "s/[ &]/\\n/g" | sed "/^$/d" >$COOKIES_LIST + fi + fi + + CODE_GEN_CONF=/scripts/logs/code_gen_conf.list + echo "生成互助消息需要使用的到的 logs/code_gen_conf.list 文件,后续需要自己根据说明维护更新删除..." + if [ ! -f "$CODE_GEN_CONF" ]; then + ( + cat <$CODE_GEN_CONF + else + echo "logs/code_gen_conf.list 文件已经存在跳过初始化操作" + fi + + echo "容器jd_bot交互所需环境已配置安装已完成..." + curl -sX POST "https://api.telegram.org/bot$TG_BOT_TOKEN/sendMessage" -d "chat_id=$TG_USER_ID&text=恭喜🎉你获得feature容器jd_bot交互所需环境已配置安装已完成,并启用。请发送 /help 查看使用帮助。如需禁用请在docker-compose.yml配置 DISABLE_BOT_COMMAND=True" >>/dev/null + +fi + +#echo "暂停更新配置,不要尝试删掉这个文件,你的容器可能会起不来" +#echo '' >/scripts/logs/pull.lock + +echo "定义定时任务合并处理用到的文件路径..." +defaultListFile="/scripts/docker/$DEFAULT_LIST_FILE" +echo "默认文件定时任务文件路径为 ${defaultListFile}" +mergedListFile="/scripts/docker/merged_list_file.sh" +echo "合并后定时任务文件路径为 ${mergedListFile}" + +echo "第1步将默认定时任务列表添加到并后定时任务文件..." +cat $defaultListFile >$mergedListFile + +echo "第2步判断是否存在自定义任务任务列表并追加..." +if [ $CUSTOM_LIST_FILE ]; then + echo "您配置了自定义任务文件:$CUSTOM_LIST_FILE,自定义任务类型为:$CUSTOM_LIST_MERGE_TYPE..." + # 无论远程还是本地挂载, 均复制到 $customListFile + customListFile="/scripts/docker/custom_list_file.sh" + echo "自定义定时任务文件临时工作路径为 ${customListFile}" + if expr "$CUSTOM_LIST_FILE" : 'http.*' &>/dev/null; then + echo "自定义任务文件为远程脚本,开始下载自定义远程任务。" + wget -O $customListFile $CUSTOM_LIST_FILE + echo "下载完成..." + elif [ -f /scripts/docker/$CUSTOM_LIST_FILE ]; then + echo "自定义任务文件为本地挂载。" + cp /scripts/docker/$CUSTOM_LIST_FILE $customListFile + fi + + if [ -f "$customListFile" ]; then + if [ $CUSTOM_LIST_MERGE_TYPE == "append" ]; then + echo "合并默认定时任务文件:$DEFAULT_LIST_FILE 和 自定义定时任务文件:$CUSTOM_LIST_FILE" + echo -e "" >>$mergedListFile + cat $customListFile >>$mergedListFile + elif [ $CUSTOM_LIST_MERGE_TYPE == "overwrite" ]; then + echo "配置了自定义任务文件:$CUSTOM_LIST_FILE,自定义任务类型为:$CUSTOM_LIST_MERGE_TYPE..." + cat $customListFile >$mergedListFile + else + echo "配置配置了错误的自定义定时任务类型:$CUSTOM_LIST_MERGE_TYPE,自定义任务类型为只能为append或者overwrite..." + fi + else + echo "配置的自定义任务文件:$CUSTOM_LIST_FILE未找到,使用默认配置$DEFAULT_LIST_FILE..." + fi +else + echo "当前只使用了默认定时任务文件 $DEFAULT_LIST_FILE ..." +fi + +echo "第3步判断是否配置了随机延迟参数..." +if [ $RANDOM_DELAY_MAX ]; then + if [ $RANDOM_DELAY_MAX -ge 1 ]; then + echo "已设置随机延迟为 $RANDOM_DELAY_MAX , 设置延迟任务中..." + sed -i "/\(jd_bean_sign.js\|jd_blueCoin.js\|jd_joy_reward.js\|jd_joy_steal.js\|jd_joy_feedPets.js\|jd_car_exchange.js\)/!s/node/sleep \$((RANDOM % \$RANDOM_DELAY_MAX)); node/g" $mergedListFile + fi +else + echo "未配置随机延迟对应的环境变量,故不设置延迟任务..." +fi + +echo "第4步判断是否配置自定义shell执行脚本..." +if [ 0"$CUSTOM_SHELL_FILE" = "0" ]; then + echo "未配置自定shell脚本文件,跳过执行。" +else + if expr "$CUSTOM_SHELL_FILE" : 'http.*' &>/dev/null; then + echo "自定义shell脚本为远程脚本,开始下载自定义远程脚本。" + wget -O /scripts/docker/shell_script_mod.sh $CUSTOM_SHELL_FILE + echo "下载完成,开始执行..." + echo "#远程自定义shell脚本追加定时任务" >>$mergedListFile + sh -x /scripts/docker/shell_script_mod.sh + echo "自定义远程shell脚本下载并执行结束。" + else + if [ ! -f $CUSTOM_SHELL_FILE ]; then + echo "自定义shell脚本为docker挂载脚本文件,但是指定挂载文件不存在,跳过执行。" + else + echo "docker挂载的自定shell脚本,开始执行..." + echo "#docker挂载自定义shell脚本追加定时任务" >>$mergedListFile + sh -x $CUSTOM_SHELL_FILE + echo "docker挂载的自定shell脚本,执行结束。" + fi + fi +fi + +echo "第5步删除不运行的脚本任务..." +if [ $DO_NOT_RUN_SCRIPTS ]; then + echo "您配置了不运行的脚本:$DO_NOT_RUN_SCRIPTS" + arr=${DO_NOT_RUN_SCRIPTS//&/ } + for item in $arr; do + sed -ie '/'"${item}"'/d' ${mergedListFile} + done + +fi + +echo "第6步设定下次运行docker_entrypoint.sh时间..." +echo "删除原有docker_entrypoint.sh任务" +sed -ie '/'docker_entrypoint.sh'/d' ${mergedListFile} + +# 12:00前生成12:00后的cron,12:00后生成第二天12:00前的cron,一天只更新两次代码 +if [ $(date +%-H) -lt 12 ]; then + random_h=$(($RANDOM % 12 + 12)) +else + random_h=$(($RANDOM % 12)) +fi +random_m=$(($RANDOM % 60)) + +echo "设定 docker_entrypoint.sh cron为:" +echo -e "\n# 必须要的默认定时任务请勿删除" >>$mergedListFile +echo -e "${random_m} ${random_h} * * * docker_entrypoint.sh >> /scripts/logs/default_task.log 2>&1" | tee -a $mergedListFile + +echo "第7步 自动助力" +if [ -n "$ENABLE_AUTO_HELP" ]; then + #直接判断变量,如果未配置,会导致sh抛出一个错误,所以加了上面一层 + if [ "$ENABLE_AUTO_HELP" = "true" ]; then + echo "开启自动助力" + #在所有脚本执行前,先执行助力码导出 + sed -i 's/node/ . \/scripts\/docker\/auto_help.sh export > \/scripts\/logs\/auto_help_export.log \&\& node /g' ${mergedListFile} + else + echo "未开启自动助力" + fi +fi + +echo "第8步增加 |ts 任务日志输出时间戳..." +sed -i "/\( ts\| |ts\|| ts\)/!s/>>/\|ts >>/g" $mergedListFile + +echo "第9步执行proc_file.sh脚本任务..." +sh /scripts/docker/proc_file.sh + +echo "第10步加载最新的定时任务文件..." +if [[ -f /usr/bin/jd_bot && -z "$DISABLE_SPNODE" ]]; then + echo "bot交互与spnode 前置条件成立,替换任务列表的node指令为spnode" + sed -i "s/ node / spnode /g" $mergedListFile + #conc每个cookies独立并行执行脚本示例,cookies数量多使用该功能可能导致内存爆掉,默认不开启 有需求,请在自定义shell里面实现 + #sed -i "/\(jd_xtg.js\|jd_car_exchange.js\)/s/spnode/spnode conc/g" $mergedListFile +fi +crontab $mergedListFile + +echo "第11步将仓库的docker_entrypoint.sh脚本更新至系统/usr/local/bin/docker_entrypoint.sh内..." +cat /scripts/docker/docker_entrypoint.sh >/usr/local/bin/docker_entrypoint.sh + +echo "发送通知" +export NOTIFY_CONTENT="" +cd /scripts/docker +node notify_docker_user.js diff --git a/docker/docker_entrypoint.sh b/docker/docker_entrypoint.sh new file mode 100644 index 0000000..6c9852c --- /dev/null +++ b/docker/docker_entrypoint.sh @@ -0,0 +1,57 @@ +#!/bin/sh +set -e + +#获取配置的自定义参数 +if [ -n "$1" ]; then + run_cmd=$1 +fi + +( +if [ -f "/scripts/logs/pull.lock" ]; then + echo "存在更新锁定文件,跳过git pull操作..." +else + echo "设定远程仓库地址..." + cd /scripts + git remote set-url origin "$REPO_URL" + git reset --hard + echo "git pull拉取最新代码..." + git -C /scripts pull --rebase + echo "npm install 安装最新依赖" + npm install --prefix /scripts +fi +) || exit 0 + +# 默认启动telegram交互机器人的条件 +# 确认容器启动时调用的docker_entrypoint.sh +# 必须配置TG_BOT_TOKEN、TG_USER_ID, +# 且未配置DISABLE_BOT_COMMAND禁用交互, +# 且未配置自定义TG_API_HOST,因为配置了该变量,说明该容器环境可能并能科学的连到telegram服务器 +if [[ -n "$run_cmd" && -n "$TG_BOT_TOKEN" && -n "$TG_USER_ID" && -z "$DISABLE_BOT_COMMAND" && -z "$TG_API_HOST" ]]; then + ENABLE_BOT_COMMAND=True +else + ENABLE_BOT_COMMAND=False +fi + +echo "------------------------------------------------执行定时任务任务shell脚本------------------------------------------------" +#测试 +# sh /jd_docker/docker/default_task.sh "$ENABLE_BOT_COMMAND" "$run_cmd" +#合并 +sh /scripts/docker/default_task.sh "$ENABLE_BOT_COMMAND" "$run_cmd" +echo "--------------------------------------------------默认定时任务执行完成---------------------------------------------------" + +if [ -n "$run_cmd" ]; then + # 增加一层jd_bot指令已经正确安装成功校验 + # 以上条件都满足后会启动jd_bot交互,否还是按照以前的模式启动,最大程度避免现有用户改动调整 + if [[ "$ENABLE_BOT_COMMAND" == "True" && -f /usr/bin/jd_bot ]]; then + echo "启动crontab定时任务主进程..." + crond + echo "启动telegram bot指令交主进程..." + jd_bot + else + echo "启动crontab定时任务主进程..." + crond -f + fi + +else + echo "默认定时任务执行结束。" +fi diff --git a/docker/example/custom-append.yml b/docker/example/custom-append.yml new file mode 100644 index 0000000..eeaa8ed --- /dev/null +++ b/docker/example/custom-append.yml @@ -0,0 +1,62 @@ +jd_scripts: + image: lxk0301/jd_scripts + # 配置服务器资源约束。此例子中服务被限制为使用内存不超过200M以及cpu不超过0.2(单核的20%) + # 经过实际测试,建议不低于200M + # deploy: + # resources: + # limits: + # cpus: '0.2' + # memory: 200M + container_name: jd_scripts + restart: always + volumes: + - ./my_crontab_list.sh:/scripts/docker/my_crontab_list.sh + - ./logs:/scripts/logs + tty: true + # 因为更换仓库地址可能git pull的dns解析不到,可以在配置追加hosts + extra_hosts: + - "gitee.com:180.97.125.228" + - "github.com:13.229.188.59" + - "raw.githubusercontent.com:151.101.228.133" + environment: + #脚本更新仓库地址,配置了会切换到对应的地址 + - REPO_URL=git@gitee.com:lxk0301/jd_scripts.git + # 注意环境变量填写值的时候一律不需要引号(""或者'')下面这些只是示例,根据自己的需求增加删除 + #jd cookies + # 例: JD_COOKIE=pt_key=XXX;pt_pin=XXX; + # 例(多账号): JD_COOKIE=pt_key=XXX;pt_pin=XXX;&pt_key=XXX;pt_pin=XXX;&pt_key=XXX;pt_pin=XXX; + - JD_COOKIE= + #微信server酱通知 + - PUSH_KEY= + #Bark App通知 + - BARK_PUSH= + #telegram机器人通知 + - TG_BOT_TOKEN= + - TG_USER_ID= + #钉钉机器人通知 + - DD_BOT_TOKEN= + - DD_BOT_SECRET= + #企业微信机器人通知 + - QYWX_KEY= + #京东种豆得豆 + - PLANT_BEAN_SHARECODES= + #京东农场 + # 例: FRUITSHARECODES=京东农场的互助码 + - FRUITSHARECODES= + #京东萌宠 + # 例: PETSHARECODES=东东萌宠的互助码 + - PETSHARECODES= + # 宠汪汪的喂食数量 + - JOY_FEED_COUNT= + #东东超市 + # - SUPERMARKET_SHARECODES= + #兑换多少数量的京豆(20,或者1000京豆,或者其他奖品的文字) + # 例: MARKET_COIN_TO_BEANS=1000 + - MARKET_COIN_TO_BEANS= + #如果设置了 RANDOM_DELAY_MAX ,则会启用随机延迟功能,延迟随机 0 到 RANDOM_DELAY_MAX-1 秒。如果不设置此项,则不使用延迟。 + #并不是所有的脚本都会被启用延迟,因为有一些脚本需要整点触发。延迟的目的有两个,1是降低抢占cpu资源几率,2是降低检查风险(主要是1) + #填写数字,单位为秒,比如写为 RANDOM_DELAY_MAX=30 就是随机产生0到29之间的一个秒数,执行延迟的意思。 + - RANDOM_DELAY_MAX=120 + #使用自定义定任务追加默认任务之后,上面volumes挂载之后这里配置对应的文件名 + - CUSTOM_LIST_FILE=my_crontab_list.sh + diff --git a/docker/example/custom-overwrite.yml b/docker/example/custom-overwrite.yml new file mode 100644 index 0000000..dd0a6a5 --- /dev/null +++ b/docker/example/custom-overwrite.yml @@ -0,0 +1,62 @@ +jd_scripts: + image: lxk0301/jd_scripts + # 配置服务器资源约束。此例子中服务被限制为使用内存不超过200M以及cpu不超过0.2(单核的20%) + # 经过实际测试,建议不低于200M + # deploy: + # resources: + # limits: + # cpus: '0.2' + # memory: 200M + container_name: jd_scripts + restart: always + volumes: + - ./my_crontab_list.sh:/scripts/docker/my_crontab_list.sh + - ./logs:/scripts/logs + tty: true + # 因为更换仓库地址可能git pull的dns解析不到,可以在配置追加hosts + extra_hosts: + - "gitee.com:180.97.125.228" + - "github.com:13.229.188.59" + - "raw.githubusercontent.com:151.101.228.133" + environment: + #脚本更新仓库地址,配置了会切换到对应的地址 + - REPO_URL=git@gitee.com:lxk0301/jd_scripts.git + # 注意环境变量填写值的时候一律不需要引号(""或者'')下面这些只是示例,根据自己的需求增加删除 + #jd cookies + # 例: JD_COOKIE=pt_key=XXX;pt_pin=XXX; + #例(多账号): JD_COOKIE=pt_key=XXX;pt_pin=XXX;&pt_key=XXX;pt_pin=XXX;&pt_key=XXX;pt_pin=XXX; + - JD_COOKIE= + #微信server酱通知 + - PUSH_KEY= + #Bark App通知 + - BARK_PUSH= + #telegram机器人通知 + - TG_BOT_TOKEN= + - TG_USER_ID= + #钉钉机器人通知 + - DD_BOT_TOKEN= + - DD_BOT_SECRET= + #企业微信机器人通知 + - QYWX_KEY= + #京东种豆得豆 + - PLANT_BEAN_SHARECODES= + #京东农场 + # 例: FRUITSHARECODES=京东农场的互助码 + - FRUITSHARECODES= + #京东萌宠 + # 例: PETSHARECODES=东东萌宠的互助码 + - PETSHARECODES= + # 宠汪汪的喂食数量 + - JOY_FEED_COUNT= + #东东超市 + # - SUPERMARKET_SHARECODES= + #兑换多少数量的京豆(20,或者1000京豆,或者其他奖品的文字) + # 例: MARKET_COIN_TO_BEANS=1000 + - MARKET_COIN_TO_BEANS= + #如果设置了 RANDOM_DELAY_MAX ,则会启用随机延迟功能,延迟随机 0 到 RANDOM_DELAY_MAX-1 秒。如果不设置此项,则不使用延迟。 + #并不是所有的脚本都会被启用延迟,因为有一些脚本需要整点触发。延迟的目的有两个,1是降低抢占cpu资源几率,2是降低检查风险(主要是1) + #填写数字,单位为秒,比如写为 RANDOM_DELAY_MAX=30 就是随机产生0到29之间的一个秒数,执行延迟的意思。 + - RANDOM_DELAY_MAX=120 + #使用自定义定任务覆盖默认任务,上面volumes挂载之后这里配置对应的文件名,和自定义文件使用方式为overwrite + - CUSTOM_LIST_FILE=my_crontab_list.sh + - CUSTOM_LIST_MERGE_TYPE=overwrite diff --git a/docker/example/default.yml b/docker/example/default.yml new file mode 100644 index 0000000..3ff4995 --- /dev/null +++ b/docker/example/default.yml @@ -0,0 +1,59 @@ +jd_scripts: + image: lxk0301/jd_scripts + # 配置服务器资源约束。此例子中服务被限制为使用内存不超过200M以及cpu不超过0.2(单核的20%) + # 经过实际测试,建议不低于200M + # deploy: + # resources: + # limits: + # cpus: '0.2' + # memory: 200M + container_name: jd_scripts + restart: always + volumes: + - ./logs:/scripts/logs + tty: true + # 因为更换仓库地址可能git pull的dns解析不到,可以在配置追加hosts + extra_hosts: + - "gitee.com:180.97.125.228" + - "github.com:13.229.188.59" + - "raw.githubusercontent.com:151.101.228.133" + environment: + #脚本更新仓库地址,配置了会切换到对应的地址 + - REPO_URL=git@gitee.com:lxk0301/jd_scripts.git + # 注意环境变量填写值的时候一律不需要引号(""或者'')下面这些只是示例,根据自己的需求增加删除 + #jd cookies + # 例: JD_COOKIE=pt_key=XXX;pt_pin=XXX; + # 例(多账号): JD_COOKIE=pt_key=XXX;pt_pin=XXX;&pt_key=XXX;pt_pin=XXX;&pt_key=XXX;pt_pin=XXX; + - JD_COOKIE= + #微信server酱通知 + - PUSH_KEY= + #Bark App通知 + - BARK_PUSH= + #telegram机器人通知 + - TG_BOT_TOKEN= + - TG_USER_ID= + #钉钉机器人通知 + - DD_BOT_TOKEN= + - DD_BOT_SECRET= + #企业微信机器人通知 + - QYWX_KEY= + #京东种豆得豆 + - PLANT_BEAN_SHARECODES= + #京东农场 + # 例: FRUITSHARECODES=京东农场的互助码 + - FRUITSHARECODES= + #京东萌宠 + # 例: PETSHARECODES=东东萌宠的互助码 + - PETSHARECODES= + # 宠汪汪的喂食数量 + - JOY_FEED_COUNT= + #东东超市 + # - SUPERMARKET_SHARECODES= + #兑换多少数量的京豆(20,或者1000京豆,或者其他奖品的文字) + # 例: MARKET_COIN_TO_BEANS=1000 + - MARKET_COIN_TO_BEANS= + + #如果设置了 RANDOM_DELAY_MAX ,则会启用随机延迟功能,延迟随机 0 到 RANDOM_DELAY_MAX-1 秒。如果不设置此项,则不使用延迟。 + #并不是所有的脚本都会被启用延迟,因为有一些脚本需要整点触发。延迟的目的有两个,1是降低抢占cpu资源几率,2是降低检查风险(主要是1) + #填写数字,单位为秒,比如写为 RANDOM_DELAY_MAX=30 就是随机产生0到29之间的一个秒数,执行延迟的意思。 + - RANDOM_DELAY_MAX=120 diff --git a/docker/example/docker多账户使用独立容器使用说明.md b/docker/example/docker多账户使用独立容器使用说明.md new file mode 100644 index 0000000..4fd879d --- /dev/null +++ b/docker/example/docker多账户使用独立容器使用说明.md @@ -0,0 +1,83 @@ +### 使用此方式,请先理解学会使用[docker办法一](https://github.com/LXK9301/jd_scripts/tree/master/docker#%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E7%9B%AE%E5%BD%95jd_scripts%E7%94%A8%E4%BA%8E%E5%AD%98%E6%94%BE%E5%A4%87%E4%BB%BD%E9%85%8D%E7%BD%AE%E7%AD%89%E6%95%B0%E6%8D%AE%E8%BF%81%E7%A7%BB%E9%87%8D%E8%A3%85%E7%9A%84%E6%97%B6%E5%80%99%E5%8F%AA%E9%9C%80%E8%A6%81%E5%A4%87%E4%BB%BD%E6%95%B4%E4%B8%AAjd_scripts%E7%9B%AE%E5%BD%95%E5%8D%B3%E5%8F%AF)的使用方式 +> 发现有人好像希望不同账户任务并发执行,不想一个账户执行完了才能再执行另一个,这里写一个`docker办法一`的基础上实现方式,其实就是不同账户创建不同的容器,他们互不干扰单独定时执行自己的任务。 +配置使用起来还是比较简单的,具体往下看 +### 文件夹目录参考 +![image](https://user-images.githubusercontent.com/6993269/97781779-885ae700-1bc8-11eb-93a4-b274cbd6062c.png) +### 具体使用说明直接在图片标注了,文件参考[图片下方](https://github.com/LXK9301/jd_scripts/new/master/docker#docker-composeyml%E6%96%87%E4%BB%B6%E5%8F%82%E8%80%83),配置完成后的[执行命令]() +![image](https://user-images.githubusercontent.com/6993269/97781610-a1af6380-1bc7-11eb-9397-903b47f5ad6b.png) +#### `docker-compose.yml`文件参考 +```yaml +version: "3" +services: + jd_scripts1: #默认 + image: lxk0301/jd_scripts + # 配置服务器资源约束。此例子中服务被限制为使用内存不超过200M以及cpu不超过 0.2(单核的20%) + # 经过实际测试,建议不低于200M + # deploy: + # resources: + # limits: + # cpus: '0.2' + # memory: 200M + restart: always + container_name: jd_scripts1 + tty: true + volumes: + - ./logs1:/scripts/logs + environment: + - JD_COOKIE=pt_key=AAJfjaNrADAS8ygfgIsOxxxxxxxKpfDaZ2pSBOYTxtPqLK8U1Q;pt_pin=lxxxxxx5; + - TG_BOT_TOKEN=130xxxx280:AAExxxxxxWP10zNf91WQ + - TG_USER_ID=12xxxx206 + # 互助助码等参数可自行增加,如下。 + # 京东种豆得豆 + # - PLANT_BEAN_SHARECODES= + + jd_scripts2: #默认 + image: lxk0301/jd_scripts + restart: always + container_name: jd_scripts2 + tty: true + volumes: + - ./logs2:/scripts/logs + environment: + - JD_COOKIE=pt_key=AAJfjaNrADAS8ygfgIsOxxxxxxxKpfDaZ2pSBOYTxtPqLK8U1Q;pt_pin=lxxxxxx5; + - TG_BOT_TOKEN=130xxxx280:AAExxxxxxWP10zNf91WQ + - TG_USER_ID=12xxxx206 + jd_scripts4: #自定义追加默认之后 + image: lxk0301/jd_scripts + restart: always + container_name: jd_scripts4 + tty: true + volumes: + - ./logs4:/scripts/logs + - ./my_crontab_list4.sh:/scripts/docker/my_crontab_list.sh + environment: + - JD_COOKIE=pt_key=AAJfjaNrADAS8ygfgIsOxxxxxxxKpfDaZ2pSBOYTxtPqLK8U1Q;pt_pin=lxxxxxx5; + - TG_BOT_TOKEN=130xxxx280:AAExxxxxxWP10zNf91WQ + - TG_USER_ID=12xxxx206 + - CUSTOM_LIST_FILE=my_crontab_list.sh + jd_scripts5: #自定义覆盖默认 + image: lxk0301/jd_scripts + restart: always + container_name: jd_scripts5 + tty: true + volumes: + - ./logs5:/scripts/logs + - ./my_crontab_list5.sh:/scripts/docker/my_crontab_list.sh + environment: + - JD_COOKIE=pt_key=AAJfjaNrADAS8ygfgIsOxxxxxxxKpfDaZ2pSBOYTxtPqLK8U1Q;pt_pin=lxxxxxx5; + - TG_BOT_TOKEN=130xxxx280:AAExxxxxxWP10zNf91WQ + - TG_USER_ID=12xxxx206 + - CUSTOM_LIST_FILE=my_crontab_list.sh + - CUSTOM_LIST_MERGE_TYPE=overwrite + +``` +#### 目录文件配置好之后在 `jd_scripts_multi`目录执行 + `docker-compose up -d` 启动; + `docker-compose logs` 打印日志; + `docker-compose pull` 更新镜像; + `docker-compose stop` 停止容器; + `docker-compose restart` 重启容器; + `docker-compose down` 停止并删除容器; + ![image](https://user-images.githubusercontent.com/6993269/97781935-8fcec000-1bc9-11eb-9d1a-d219e7a1caa9.png) + + diff --git a/docker/example/jd_scripts.custom-append.syno.json b/docker/example/jd_scripts.custom-append.syno.json new file mode 100644 index 0000000..a2da16f --- /dev/null +++ b/docker/example/jd_scripts.custom-append.syno.json @@ -0,0 +1,65 @@ +{ + "cap_add" : [], + "cap_drop" : [], + "cmd" : "", + "cpu_priority" : 50, + "devices" : null, + "enable_publish_all_ports" : false, + "enable_restart_policy" : true, + "enabled" : true, + "env_variables" : [ + { + "key" : "PATH", + "value" : "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + }, + { + "key" : "CDN_JD_DAILYBONUS", + "value" : "true" + }, + { + "key" : "JD_COOKIE", + "value" : "pt_key=xxx;pt_pin=xxx;" + }, + { + "key" : "PUSH_KEY", + "value" : "" + }, + { + "key" : "CUSTOM_LIST_FILE", + "value" : "my_crontab_list.sh" + } + ], + "exporting" : false, + "id" : "3a2f6f27c23f93bc104585c22569c760cc9ce82df09cdb41d53b491fe1d0341c", + "image" : "lxk0301/jd_scripts", + "is_ddsm" : false, + "is_package" : false, + "links" : [], + "memory_limit" : 0, + "name" : "jd_scripts", + "network" : [ + { + "driver" : "bridge", + "name" : "bridge" + } + ], + "network_mode" : "default", + "port_bindings" : [], + "privileged" : false, + "shortcut" : { + "enable_shortcut" : false + }, + "use_host_network" : false, + "volume_bindings" : [ + { + "host_volume_file" : "/docker/jd_scripts/my_crontab_list.sh", + "mount_point" : "/scripts/docker/my_crontab_list.sh", + "type" : "rw" + }, + { + "host_volume_file" : "/docker/jd_scripts/logs", + "mount_point" : "/scripts/logs", + "type" : "rw" + } + ] +} diff --git a/docker/example/jd_scripts.custom-overwrite.syno.json b/docker/example/jd_scripts.custom-overwrite.syno.json new file mode 100644 index 0000000..e4e05fb --- /dev/null +++ b/docker/example/jd_scripts.custom-overwrite.syno.json @@ -0,0 +1,69 @@ +{ + "cap_add" : [], + "cap_drop" : [], + "cmd" : "", + "cpu_priority" : 50, + "devices" : null, + "enable_publish_all_ports" : false, + "enable_restart_policy" : true, + "enabled" : true, + "env_variables" : [ + { + "key" : "PATH", + "value" : "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + }, + { + "key" : "CDN_JD_DAILYBONUS", + "value" : "true" + }, + { + "key" : "JD_COOKIE", + "value" : "pt_key=xxx;pt_pin=xxx;" + }, + { + "key" : "PUSH_KEY", + "value" : "" + }, + { + "key" : "CUSTOM_LIST_FILE", + "value" : "my_crontab_list.sh" + }, + { + "key" : "CUSTOM_LIST_MERGE_TYPE", + "value" : "overwrite" + } + ], + "exporting" : false, + "id" : "3a2f6f27c23f93bc104585c22569c760cc9ce82df09cdb41d53b491fe1d0341c", + "image" : "lxk0301/jd_scripts", + "is_ddsm" : false, + "is_package" : false, + "links" : [], + "memory_limit" : 0, + "name" : "jd_scripts", + "network" : [ + { + "driver" : "bridge", + "name" : "bridge" + } + ], + "network_mode" : "default", + "port_bindings" : [], + "privileged" : false, + "shortcut" : { + "enable_shortcut" : false + }, + "use_host_network" : false, + "volume_bindings" : [ + { + "host_volume_file" : "/docker/jd_scripts/my_crontab_list.sh", + "mount_point" : "/scripts/docker/my_crontab_list.sh", + "type" : "rw" + }, + { + "host_volume_file" : "/docker/jd_scripts/logs", + "mount_point" : "/scripts/logs", + "type" : "rw" + } + ] +} diff --git a/docker/example/jd_scripts.syno.json b/docker/example/jd_scripts.syno.json new file mode 100644 index 0000000..189b047 --- /dev/null +++ b/docker/example/jd_scripts.syno.json @@ -0,0 +1,83 @@ +{ + "cap_add" : null, + "cap_drop" : null, + "cmd" : "", + "cpu_priority" : 0, + "devices" : null, + "enable_publish_all_ports" : false, + "enable_restart_policy" : true, + "enabled" : false, + "env_variables" : [ + { + "key" : "PATH", + "value" : "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + }, + { + "key" : "CDN_JD_DAILYBONUS", + "value" : "true" + }, + { + "key" : "JD_COOKIE", + "value" : "pt_key=AAJfjaNrADASxxxxxxxKpfDaZ2pSBOYTxtPqLK8U1Q;pt_pin=lxxxxx5;" + }, + { + "key" : "TG_BOT_TOKEN", + "value" : "13xxxxxx80:AAEkNxxxxxxzNf91WQ" + }, + { + "key" : "TG_USER_ID", + "value" : "12xxxx206" + }, + { + "key" : "PLANT_BEAN_SHARECODES", + "value" : "" + }, + { + "key" : "FRUITSHARECODES", + "value" : "" + }, + { + "key" : "PETSHARECODES", + "value" : "" + }, + { + "key" : "SUPERMARKET_SHARECODES", + "value" : "" + }, + { + "key" : "CRONTAB_LIST_FILE", + "value" : "crontab_list.sh" + } + ], + "exporting" : false, + "id" : "18af38bc0ac37a40e4b9608a86fef56c464577cc160bbdddec90155284fcf4e5", + "image" : "lxk0301/jd_scripts", + "is_ddsm" : false, + "is_package" : false, + "links" : [], + "memory_limit" : 0, + "name" : "jd_scripts", + "network" : [ + { + "driver" : "bridge", + "name" : "bridge" + } + ], + "network_mode" : "default", + "port_bindings" : [], + "privileged" : false, + "shortcut" : { + "enable_shortcut" : false, + "enable_status_page" : false, + "enable_web_page" : false, + "web_page_url" : "" + }, + "use_host_network" : false, + "volume_bindings" : [ + { + "host_volume_file" : "/docker/jd_scripts/logs", + "mount_point" : "/scripts/logs", + "type" : "rw" + } + ] +} diff --git a/docker/example/multi.yml b/docker/example/multi.yml new file mode 100644 index 0000000..a02de0d --- /dev/null +++ b/docker/example/multi.yml @@ -0,0 +1,62 @@ +version: "3" +services: + jd_scripts1: #默认 + image: lxk0301/jd_scripts + # 配置服务器资源约束。此例子中服务被限制为使用内存不超过200M以及cpu不超过 0.2(单核的20%) + # 经过实际测试,建议不低于200M + # deploy: + # resources: + # limits: + # cpus: '0.2' + # memory: 200M + restart: always + container_name: jd_scripts1 + tty: true + volumes: + - ./logs1:/scripts/logs + environment: + - JD_COOKIE=pt_key=AAJfjaNrADAS8ygfgIsOxxxxxxxKpfDaZ2pSBOYTxtPqLK8U1Q;pt_pin=lxxxxxx5; + - TG_BOT_TOKEN=130xxxx280:AAExxxxxxWP10zNf91WQ + - TG_USER_ID=12xxxx206 + # 互助助码等参数可自行增加,如下。 + # 京东种豆得豆 + # - PLANT_BEAN_SHARECODES= + + jd_scripts2: #默认 + image: lxk0301/jd_scripts + restart: always + container_name: jd_scripts2 + tty: true + volumes: + - ./logs2:/scripts/logs + environment: + - JD_COOKIE=pt_key=AAJfjaNrADAS8ygfgIsOxxxxxxxKpfDaZ2pSBOYTxtPqLK8U1Q;pt_pin=lxxxxxx5; + - TG_BOT_TOKEN=130xxxx280:AAExxxxxxWP10zNf91WQ + - TG_USER_ID=12xxxx206 + jd_scripts4: #自定义追加默认之后 + image: lxk0301/jd_scripts + restart: always + container_name: jd_scripts4 + tty: true + volumes: + - ./logs4:/scripts/logs + - ./my_crontab_list4.sh:/scripts/docker/my_crontab_list.sh + environment: + - JD_COOKIE=pt_key=AAJfjaNrADAS8ygfgIsOxxxxxxxKpfDaZ2pSBOYTxtPqLK8U1Q;pt_pin=lxxxxxx5; + - TG_BOT_TOKEN=130xxxx280:AAExxxxxxWP10zNf91WQ + - TG_USER_ID=12xxxx206 + - CUSTOM_LIST_FILE=my_crontab_list.sh + jd_scripts5: #自定义覆盖默认 + image: lxk0301/jd_scripts + restart: always + container_name: jd_scripts5 + tty: true + volumes: + - ./logs5:/scripts/logs + - ./my_crontab_list5.sh:/scripts/docker/my_crontab_list.sh + environment: + - JD_COOKIE=pt_key=AAJfjaNrADAS8ygfgIsOxxxxxxxKpfDaZ2pSBOYTxtPqLK8U1Q;pt_pin=lxxxxxx5; + - TG_BOT_TOKEN=130xxxx280:AAExxxxxxWP10zNf91WQ + - TG_USER_ID=12xxxx206 + - CUSTOM_LIST_FILE=my_crontab_list.sh + - CUSTOM_LIST_MERGE_TYPE=overwrite diff --git a/docker/notify_docker_user.js b/docker/notify_docker_user.js new file mode 100644 index 0000000..7ca2b0e --- /dev/null +++ b/docker/notify_docker_user.js @@ -0,0 +1,20 @@ +const notify = require('../sendNotify'); +const fs = require('fs'); +const notifyPath = '/scripts/logs/notify.txt'; +async function image_update_notify() { + if (fs.existsSync(notifyPath)) { + const content = await fs.readFileSync(`${notifyPath}`, 'utf8');//读取notify.txt内容 + if (process.env.NOTIFY_CONTENT && !content.includes(process.env.NOTIFY_CONTENT)) { + await notify.sendNotify("⚠️Docker镜像版本更新通知⚠️", process.env.NOTIFY_CONTENT); + await fs.writeFileSync(`${notifyPath}`, process.env.NOTIFY_CONTENT); + } + } else { + if (process.env.NOTIFY_CONTENT) { + notify.sendNotify("⚠️Docker镜像版本更新通知⚠️", process.env.NOTIFY_CONTENT) + await fs.writeFileSync(`${notifyPath}`, process.env.NOTIFY_CONTENT); + } + } +} +!(async() => { + await image_update_notify(); +})().catch((e) => console.log(e)) \ No newline at end of file diff --git a/docker/proc_file.sh b/docker/proc_file.sh new file mode 100644 index 0000000..89f4627 --- /dev/null +++ b/docker/proc_file.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +if [[ -f /usr/bin/jd_bot && -z "$DISABLE_SPNODE" ]]; then + CMD="spnode" +else + CMD="node" +fi + +echo "处理jd_crazy_joy_coin任务。。。" +if [ ! $CRZAY_JOY_COIN_ENABLE ]; then + echo "默认启用jd_crazy_joy_coin杀掉jd_crazy_joy_coin任务,并重启" + eval $(ps -ef | grep "jd_crazy_joy_coin" | grep -v "grep" | awk '{print "kill "$1}') + echo '' >/scripts/logs/jd_crazy_joy_coin.log + $CMD /scripts/jd_crazy_joy_coin.js | ts >>/scripts/logs/jd_crazy_joy_coin.log 2>&1 & + echo "默认jd_crazy_joy_coin重启完成" +else + if [ $CRZAY_JOY_COIN_ENABLE = "Y" ]; then + echo "配置启用jd_crazy_joy_coin,杀掉jd_crazy_joy_coin任务,并重启" + eval $(ps -ef | grep "jd_crazy_joy_coin" | grep -v "grep" | awk '{print "kill "$1}') + echo '' >/scripts/logs/jd_crazy_joy_coin.log + $CMD /scripts/jd_crazy_joy_coin.js | ts >>/scripts/logs/jd_crazy_joy_coin.log 2>&1 & + echo "配置jd_crazy_joy_coin重启完成" + else + eval $(ps -ef | grep "jd_crazy_joy_coin" | grep -v "grep" | awk '{print "kill "$1}') + echo "已配置不启用jd_crazy_joy_coin任务,仅杀掉" + fi +fi diff --git a/jd_wsck.py b/jd_wsck.py new file mode 100644 index 0000000..88b38f2 --- /dev/null +++ b/jd_wsck.py @@ -0,0 +1,412 @@ +from requests import get, post, put, packages +import requests +from re import findall +from os.path import exists +import json +import os +import sys,re +import random,time +import base64 +import hashlib +import urllib.parse +import uuid + +packages.urllib3.disable_warnings() +from urllib.parse import unquote +""" +new Env('wskey本地转换'); +cron 57 21,9 * * * +""" +hadsend=True +UserAgent="" + +def printf(text): + print(text) + sys.stdout.flush() + +def randomuserAgent(): + global struuid,addressid,iosVer,iosV,clientVersion,iPhone,area,ADID,lng,lat + global UserAgent + struuid=''.join(random.sample(['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','a','b','c','z'], 40)) + addressid = ''.join(random.sample('1234567898647', 10)) + iosVer = ''.join(random.sample(["15.1.1","14.5.1", "14.4", "14.3", "14.2", "14.1", "14.0.1"], 1)) + iosV = iosVer.replace('.', '_') + clientVersion=''.join(random.sample(["10.3.0", "10.2.7", "10.2.4"], 1)) + iPhone = ''.join(random.sample(["8", "9", "10", "11", "12", "13"], 1)) + area=''.join(random.sample('0123456789', 2)) + '_' + ''.join(random.sample('0123456789', 4)) + '_' + ''.join(random.sample('0123456789', 5)) + '_' + ''.join(random.sample('0123456789', 5)) + ADID = ''.join(random.sample('0987654321ABCDEF', 8)) + '-' + ''.join(random.sample('0987654321ABCDEF', 4)) + '-' + ''.join(random.sample('0987654321ABCDEF', 4)) + '-' + ''.join(random.sample('0987654321ABCDEF', 4)) + '-' + ''.join(random.sample('0987654321ABCDEF', 12)) + lng='119.31991256596'+str(random.randint(100,999)) + lat='26.1187118976'+str(random.randint(100,999)) + UserAgent=f'jdapp;iPhone;10.0.4;{iosVer};{uuid};network/wifi;ADID/{ADID};model/iPhone{iPhone},1;addressid/{addressid};appBuild/167707;jdSupportDarkMode/0;Mozilla/5.0 (iPhone; CPU iPhone OS {iosV} like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/null;supportJDSHWK/1' + +def load_send(): + global send + global hadsend + cur_path = os.path.abspath(os.path.dirname(__file__)) + sys.path.append(cur_path) + if os.path.exists(cur_path + "/sendNotify.py"): + try: + from sendNotify import send + hadsend=True + except: + printf("加载sendNotify.py的通知服务失败,请检查~") + hadsend=False + else: + printf("加载通知服务失败,缺少sendNotify.py文件") + hadsend=False +load_send() + +def send_notification(title, content,summary): + # Add your own WxPusher API key here + api_key = os.environ["WP_APP_TOKEN_ONE"] + uids= os.environ["WP_APP_MAIN_UID"] + desp = '''
+
+
+

+ texttext +

+
+
+
+
+

+ 📢 +

+
+
+
+
+
+

+ despdesp +

+
+
+
''' + desp=desp.replace("texttext",title) + desp=desp.replace("despdesp" ,content.replace("\n", '
')) + + + payload = {"appToken": api_key, + "content": desp, + "summary": title+"\n"+summary, + "contentType": 2, + "uids": [uids] + } + + # Send the request + res = requests.post('http://wxpusher.zjiecode.com/api/send/message', json=payload, timeout=15).json() + if res["code"]==1000: + printf("WxPusher 发送通知消息成功!") + else: + printf(res.text) + + +def randomstr(num): + randomstr = ''.join(str(uuid.uuid4()).split('-'))[num:] + return randomstr + +def randomstr1(num): + randomstr = "" + for i in range(num): + randomstr = randomstr + random.choice("abcdefghijklmnopqrstuvwxyz0123456789") + return randomstr + +def sign_core(inarg): + key = b'80306f4370b39fd5630ad0529f77adb6' + mask = [0x37, 0x92, 0x44, 0x68, 0xA5, 0x3D, 0xCC, 0x7F, 0xBB, 0xF, 0xD9, 0x88, 0xEE, 0x9A, 0xE9, 0x5A] + array = [0 for _ in range(len(inarg))] + for i in range(len(inarg)): + r0 = int(inarg[i]) + r2 = mask[i & 0xf] + r4 = int(key[i & 7]) + r0 = r2 ^ r0 + r0 = r0 ^ r4 + r0 = r0 + r2 + r2 = r2 ^ r0 + r1 = int(key[i & 7]) + r2 = r2 ^ r1 + array[i] = r2 & 0xff + return bytes(array) + +def base64Encode(string): + return base64.b64encode(string.encode("utf-8")).decode('utf-8').translate(str.maketrans("KLMNOPQRSTABCDEFGHIJUVWXYZabcdopqrstuvwxefghijklmnyz0123456789+/", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")) + +def base64Decode(string): + return base64.b64decode(string.translate(str.maketrans("KLMNOPQRSTABCDEFGHIJUVWXYZabcdopqrstuvwxefghijklmnyz0123456789+/", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"))).decode('utf-8') + +def randomeid(): + return 'eidAaf8081218as20a2GM%s7FnfQYOecyDYLcd0rfzm3Fy2ePY4UJJOeV0Ub840kG8C7lmIqt3DTlc11fB/s4qsAP8gtPTSoxu' % randomstr1(20) + +def get_ep(jduuid : str=''): + if not jduuid: + jduuid = randomstr(16) + ts = str(int(time.time() * 1000)) + bsjduuid = base64Encode(jduuid) + area = base64Encode('%s_%s_%s_%s' % ( + random.randint(1, 10000), random.randint(1, 10000), random.randint(1, 10000), random.randint(1, 10000))) + d_model = random.choice(['Mi11Ultra', 'Mi11', 'Mi10']) + d_model = base64Encode(d_model) + return '{"hdid":"JM9F1ywUPwflvMIpYPok0tt5k9kW4ArJEU3lfLhxBqw=","ts":%s,"ridx":-1,"cipher":{"area":"%s","d_model":"%s","wifiBssid":"dW5hbw93bq==","osVersion":"CJS=","d_brand":"WQvrb21f","screen":"CtS1DIenCNqm","uuid":"%s","aid":"%s","openudid":"%s"},"ciphertype":5,"version":"1.2.0","appname":"com.jingdong.app.mall"}' % ( + int(ts) - random.randint(100, 1000), area, d_model, bsjduuid, bsjduuid, bsjduuid), jduuid, ts + +def get_sign(functionId, body, client : str="android", clientVersion : str='11.2.8',jduuid : str='') -> dict: + if isinstance(body,dict): + d=body + body=json.dumps(body) + else: + d=json.loads(body) + + if "eid" in d: + eid=d["eid"] + else: + eid=randomeid() + + ep, suid, st = get_ep(jduuid) + sv = random.choice(["102", "111", "120"]) + all_arg = "functionId=%s&body=%s&uuid=%s&client=%s&clientVersion=%s&st=%s&sv=%s" % (functionId, body, suid, client, clientVersion, st, sv) + back_bytes = sign_core(str.encode(all_arg)) + sign = hashlib.md5(base64.b64encode(back_bytes)).hexdigest() + convertUrl='functionId=%s&body=%s&clientVersion=%s&client=%s&sdkVersion=31&lang=zh_CN&harmonyOs=0&networkType=wifi&oaid=%s&eid=%s&ef=1&ep=%s&st=%s&sign=%s&sv=%s' % (functionId,body, clientVersion, client, suid, eid, urllib.parse.quote(ep), st, sign, sv) + return convertUrl + +def getcookie_wskey(key): + body = "body=%7B%22to%22%3A%22https%3A//plogin.m.jd.com/jd-mlogin/static/html/appjmp_blank.html%22%7D" + pin = findall("pin=([^;]*);", key)[0] + + for num in range(0,20): + sign = get_sign("genToken",{"url": "https://plogin.m.jd.com/jd-mlogin/static/html/appjmp_blank.html"},"android","11.2.8") + url = f"https://api.m.jd.com/client.action?functionId=genToken&{sign}" + headers = { + "cookie": key, + 'user-agent': UserAgent, + 'accept-language': 'zh-Hans-CN;q=1, en-CN;q=0.9', + 'content-type': 'application/x-www-form-urlencoded;' + } + try: + token = post(url=url, headers=headers, data=body, verify=False).json() + token=token['tokenKey'] + except Exception as error: + printf(f"【错误】{unquote(pin)}在获取token时:\n{error}") + return pin, "False" + if token!="xxx": + break + else: + printf(f"【警告】{unquote(pin)}在获取token时失败,等待5秒后重试") + time.sleep(5) + + if token=="xxx": + printf(f"【错误】{unquote(pin)}在获取token时失败,跳过") + return "Error" + url = 'https://un.m.jd.com/cgi-bin/app/appjmp' + params = { + 'tokenKey': token, + 'to': 'https://plogin.m.jd.com/cgi-bin/m/thirdapp_auth_page', + 'client_type': 'android', + 'appid': 879, + 'appup_type': 1, + } + try: + res = get(url=url, params=params, verify=False, + allow_redirects=False).cookies.get_dict() + except Exception as error: + printf(f"【错误】{unquote(pin)}在获取cookie时:\n{error}") + return "Error" + + try: + if "app_open" in res['pt_key']: + cookie = f"pt_key={res['pt_key']};pt_pin={res['pt_pin']};" + return cookie + else: + return ("Error:"+str(res)) + except Exception as error: + printf(f"【错误】{unquote(pin)}在获取cookie时:\n{str(res)}") + return "Error" + + +def subcookie(pt_pin, cookie, token): + if True: + reamrk="" + if token!="": + strptpin=pt_pin + if re.search('%', strptpin): + strptpin = unquote(strptpin, 'utf-8') + url = 'http://127.0.0.1:5600/api/envs' + headers = {'Authorization': f'Bearer {token}'} + body = { + 'searchValue': pt_pin, + 'Authorization': f'Bearer {token}' + } + datas = get(url, params=body, headers=headers).json()['data'] + old = False + isline=True + for data in datas: + if "pt_key" in data['value']: + try: + body = {"name": "JD_COOKIE", "value": cookie, "_id": data['_id']} + except: + body = {"name": "JD_COOKIE", "value": cookie, "id": data['id']} + isline=False + old = True + try: + reamrk=data['remarks'] + except: + reamrk="" + + if reamrk!="" and not reamrk is None: + strptpin=strptpin+"("+reamrk.split("@@")[0]+")" + + if old: + put(url, json=body, headers=headers) + url = 'http://127.0.0.1:5600/api/envs/enable' + if isline: + body = [body['_id']] + else: + body = [body['id']] + put(url, json=body, headers=headers) + printf(f"更新成功:{strptpin}") + else: + body = [{"value": cookie, "name": "JD_COOKIE"}] + post(url, json=body, headers=headers) + printf(f"新增成功:{strptpin}") + +def getRemark(pt_pin,token): + reamrk="" + if re.search('%', pt_pin): + strreturn=unquote(pt_pin, 'utf-8') + else: + strreturn=pt_pin + + if token!="": + url = 'http://127.0.0.1:5600/api/envs' + headers = {'Authorization': f'Bearer {token}'} + body = { + 'searchValue': pt_pin, + 'Authorization': f'Bearer {token}' + } + datas = get(url, params=body, headers=headers).json()['data'] + for data in datas: + if "pt_key" in data['value']: + try: + reamrk=data['remarks'] + break + except: + pass + if not reamrk is None and reamrk!="": + strreturn=strreturn+"("+reamrk.split("@@")[0]+")" + + return strreturn + +def main(): + printf("版本: 20230602") + printf("说明: 如果用Wxpusher通知需配置WP_APP_TOKEN_ONE和WP_APP_MAIN_UID,其中WP_APP_MAIN_UID是你的Wxpusher UID") + printf("====================================") + config="" + iswxpusher=False + counttime=0 + if os.path.exists("/ql/config/auth.json"): + config="/ql/config/auth.json" + + if os.path.exists("/ql/data/config/auth.json"): + config="/ql/data/config/auth.json" + + if config=="": + printf(f"无法判断使用环境,退出脚本!") + return + try: + if os.environ.get("WP_APP_TOKEN_ONE")==None or os.environ.get("WP_APP_MAIN_UID")==None: + printf('没有配置Wxpusher相关变量,将调用sendNotify.py发送通知') + else: + if os.environ.get("WP_APP_TOKEN_ONE")=="" or os.environ.get("WP_APP_MAIN_UID")=="": + printf('没有配置Wxpusher相关变量,将调用sendNotify.py发送通知') + else: + printf('检测到已配置Wxpusher相关变量,将使用Wxpusher发送通知') + iswxpusher=True + except: + iswxpusher=False + + resurt="" + resurt1="" + resurt2="" + summary="" + + with open(config, "r", encoding="utf-8") as f1: + token = json.load(f1)['token'] + url = 'http://127.0.0.1:5600/api/envs' + headers = {'Authorization': f'Bearer {token}'} + body = { + 'searchValue': 'JD_WSCK', + 'Authorization': f'Bearer {token}' + } + datas = get(url, params=body, headers=headers).json()['data'] + if len(datas)>0: + printf("\n===============开始转换JD_WSCK==============") + else: + printf("\n错误:没有需要转换的JD_WSCK,退出脚本!") + return + + for data in datas: + randomuserAgent() + if data['status']!=0: + continue + key = data['value'] + pin = re.findall(r'(pin=([^; ]+)(?=;?))',key)[0][1] + cookie = getcookie_wskey(key) + + if "app_open" in cookie: + #printf("转换成功:"cookie) + orgpin = cookie.split(";")[1].split("=")[1] + subcookie(orgpin, cookie, token) + newpin=getRemark(orgpin,token) + resurt1=resurt1+f"转换成功:{newpin}\n" + else: + newpin=getRemark(pin,token) + if "fake_" in cookie: + message = f"pin为{newpin}的wskey过期了!" + printf(message) + url = 'http://127.0.0.1:5600/api/envs/disable' + try: + body = [data['_id']] + except: + body = [data['id']] + put(url, json=body, headers=headers) + printf(f"禁用成功:{newpin}") + resurt2=resurt2+f"wskey已禁用:{newpin}\n" + else: + message = f"转换失败:{newpin}" + resurt2=resurt2+f"转换失败:{newpin}\n" + + + if resurt2!="": + resurt="👇👇👇👇👇转换异常👇👇👇👇👇\n"+resurt2+"\n" + summary="部分CK转换异常" + + if resurt1!="": + resurt=resurt+"👇👇👇👇👇转换成功👇👇👇👇👇\n"+resurt1 + if summary=="": + summary="全部转换成功" + + if iswxpusher: + send_notification("JD_WSCK转换结果",resurt,summary) + else: + if hadsend: + send("JD_WSCK转换结果",resurt) + else: + printf("没有启用通知!") + else: + if resurt1!="": + resurt=resurt+"👇👇👇👇👇转换成功👇👇👇👇👇\n"+resurt1 + + printf("\n\n===============转换结果==============\n") + printf(resurt) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/jd_wskey.py b/jd_wskey.py deleted file mode 100644 index 53743ea..0000000 --- a/jd_wskey.py +++ /dev/null @@ -1,680 +0,0 @@ -# -*- coding: utf-8 -* -''' -new Env('wskey转换'); -''' -import socket # 用于端口检测 -import base64 # 用于编解码 -import json # 用于Json解析 -import os # 用于导入系统变量 -import sys # 实现 sys.exit -import logging # 用于日志输出 -import time # 时间 -import re # 正则过滤 -import hmac -import struct - -WSKEY_MODE = 0 -# 0 = Default / 1 = Debug! - -if "WSKEY_DEBUG" in os.environ or WSKEY_MODE: # 判断调试模式变量 - logging.basicConfig(level=logging.DEBUG, format='%(message)s') # 设置日志为 Debug等级输出 - logger = logging.getLogger(__name__) # 主模块 - logger.debug("\nDEBUG模式开启!\n") # 消息输出 -else: # 判断分支 - logging.basicConfig(level=logging.INFO, format='%(message)s') # Info级日志 - logger = logging.getLogger(__name__) # 主模块 - -try: # 异常捕捉 - import requests # 导入HTTP模块 -except Exception as e: # 异常捕捉 - logger.info(str(e) + "\n缺少requests模块, 请执行命令:pip3 install requests\n") # 日志输出 - sys.exit(1) # 退出脚本 -os.environ['no_proxy'] = '*' # 禁用代理 -requests.packages.urllib3.disable_warnings() # 抑制错误 -try: # 异常捕捉 - from notify import send # 导入青龙消息通知模块 -except Exception as err: # 异常捕捉 - logger.debug(str(err)) # 调试日志输出 - logger.info("无推送文件") # 标准日志输出 - -ver = 21212 # 版本号 - - -# def ql_2fa(): -# ''' Demo -# if "WSKEY_TOKEN" in os.environ: -# url = 'http://127.0.0.1:{0}/api/user'.format(port) # 设置 URL地址 -# try: # 异常捕捉 -# res = s.get(url) # HTTP请求 [GET] 使用 session -# except Exception as err: # 异常捕捉 -# logger.debug(str(err)) # 调试日志输出 -# else: # 判断分支 -# if res.status_code == 200 and res.json()["code"] == 200: -# twoFactorActivated = str(res.json()["data"]["twoFactorActivated"]) -# if twoFactorActivated == 'true': -# logger.info("青龙 2FA 已开启!") -# url = 'http://127.0.0.1:{0}/api/envs?searchValue=WSKEY_Client'.format(port) # 设置 URL地址 -# res = s.get(url) -# if res.status_code == 200 and res.json()["code"] == 200: -# data = res.json()["data"] -# if len(data) == 0: -# url = 'http://127.0.0.1:{0}/api/apps' -# data = json.dumps({ -# "name": "wskey", -# "scopes": ["crons", "envs", "configs", "scripts", "logs", "dependencies", "system"] -# }) -# res = s.post(url, data=data) -# if res.status_code == 200 and res.json()["code"] == 200: -# logger.info("OpenApi创建成功") -# client_id = res.json()["data"]["client_id"] -# client_secret = res.json()["data"]["client_secret"] -# wskey_value = 'client_id={0}&client_secret={1}'.format(client_id, client_secret) -# data = [{"value": wskey_value, "name": "WSKEY_Client", "remarks": "WSKEY_OpenApi请勿删除"}] -# data = json.dumps(data) # Json格式化数据 -# url = 'http://127.0.0.1:{0}/api/envs'.format(port) # 设置 URL地址 -# s.post(url=url, data=data) # HTTP[POST]请求 使用session -# logger.info("\nWSKEY_Client变量添加完成\n--------------------\n") # 标准日志输出 -# ''' - -def ttotp(key): - key = base64.b32decode(key.upper() + '=' * ((8 - len(key)) % 8)) - counter = struct.pack('>Q', int(time.time() / 30)) - mac = hmac.new(key, counter, 'sha1').digest() - offset = mac[-1] & 0x0f - binary = struct.unpack('>L', mac[offset:offset + 4])[0] & 0x7fffffff - return str(binary)[-6:].zfill(6) - - -def ql_send(text): - if "WSKEY_SEND" in os.environ and os.environ["WSKEY_SEND"] == 'disable': - return True - else: - try: # 异常捕捉 - send('WSKEY转换', text) # 消息发送 - except Exception as err: # 异常捕捉 - logger.debug(str(err)) # Debug日志输出 - logger.info("通知发送失败") # 标准日志输出 - - -# 登录青龙 返回值 token -def get_qltoken(username, password, twoFactorSecret): # 方法 用于获取青龙 Token - logger.info("Token失效, 新登陆\n") # 日志输出 - if twoFactorSecret: - try: - twoCode = ttotp(twoFactorSecret) - except Exception as err: - logger.debug(str(err)) # Debug日志输出 - logger.info("TOTP异常") - sys.exit(1) - url = ql_url + "api/user/login" # 设置青龙地址 使用 format格式化自定义端口 - payload = json.dumps({ - 'username': username, - 'password': password - }) # HTTP请求载荷 - headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - } # HTTP请求头 设置为 Json格式 - try: # 异常捕捉 - res = requests.post(url=url, headers=headers, data=payload) # 使用 requests模块进行 HTTP POST请求 - if res.status_code == 200 and res.json()["code"] == 420: - url = ql_url + 'api/user/two-factor/login' - data = json.dumps({ - "username": username, - "password": password, - "code": twoCode - }) - res = requests.put(url=url, headers=headers, data=data) - if res.status_code == 200 and res.json()["code"] == 200: - token = res.json()["data"]['token'] # 从 res.text 返回值中 取出 Token值 - return token - else: - logger.info("两步校验失败\n") # 日志输出 - sys.exit(1) - elif res.status_code == 200 and res.json()["code"] == 200: - token = res.json()["data"]['token'] # 从 res.text 返回值中 取出 Token值 - return token - except Exception as err: - logger.debug(str(err)) # Debug日志输出 - sys.exit(1) - else: - url = ql_url + 'api/user/login' - payload = { - 'username': username, - 'password': password - } # HTTP请求载荷 - payload = json.dumps(payload) # json格式化载荷 - headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - } # HTTP请求头 设置为 Json格式 - try: # 异常捕捉 - res = requests.post(url=url, headers=headers, data=payload) # 使用 requests模块进行 HTTP POST请求 - if res.status_code == 200 and res.json()["code"] == 200: - token = res.json()["data"]['token'] # 从 res.text 返回值中 取出 Token值 - return token - else: - ql_send("青龙登录失败!") - sys.exit(1) # 脚本退出 - except Exception as err: - logger.debug(str(err)) # Debug日志输出 - logger.info("使用旧版青龙登录接口") - url = ql_url + 'api/login' # 设置青龙地址 使用 format格式化自定义端口 - payload = { - 'username': username, - 'password': password - } # HTTP请求载荷 - payload = json.dumps(payload) # json格式化载荷 - headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - } # HTTP请求头 设置为 Json格式 - try: # 异常捕捉 - res = requests.post(url=url, headers=headers, data=payload) # 使用 requests模块进行 HTTP POST请求 - token = json.loads(res.text)["data"]['token'] # 从 res.text 返回值中 取出 Token值 - except Exception as err: # 异常捕捉 - logger.debug(str(err)) # Debug日志输出 - logger.info("青龙登录失败, 请检查面板状态!") # 标准日志输出 - ql_send('青龙登陆失败, 请检查面板状态.') - sys.exit(1) # 脚本退出 - else: # 无异常执行分支 - return token # 返回 token值 - # else: # 无异常执行分支 - # return token # 返回 token值 - - -# 返回值 Token -def ql_login(): # 方法 青龙登录(获取Token 功能同上) - path = '/ql/config/auth.json' # 设置青龙 auth文件地址 - if not os.path.isfile(path): - path = '/ql/data/config/auth.json' # 尝试设置青龙 auth 新版文件地址 - if os.path.isfile(path): # 进行文件真值判断 - with open(path, "r") as file: # 上下文管理 - auth = file.read() # 读取文件 - file.close() # 关闭文件 - auth = json.loads(auth) # 使用 json模块读取 - username = auth["username"] # 提取 username - password = auth["password"] # 提取 password - token = auth["token"] # 提取 authkey - try: - twoFactorSecret = auth["twoFactorSecret"] - except Exception as err: - logger.debug(str(err)) # Debug日志输出 - twoFactorSecret = '' - if token == '': # 判断 Token是否为空 - return get_qltoken(username, password, twoFactorSecret) # 调用方法 get_qltoken 传递 username & password - else: # 判断分支 - url = ql_url + 'api/user' # 设置URL请求地址 使用 Format格式化端口 - headers = { - 'Authorization': 'Bearer {0}'.format(token), - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38' - } # 设置用于 HTTP头 - res = requests.get(url=url, headers=headers) # 调用 request模块发送 get请求 - if res.status_code == 200: # 判断 HTTP返回状态码 - return token # 有效 返回 token - else: # 判断分支 - return get_qltoken(username, password, twoFactorSecret) # 调用方法 get_qltoken 传递 username & password - else: # 判断分支 - logger.info("没有发现auth文件, 你这是青龙吗???") # 输出标准日志 - sys.exit(0) # 脚本退出 - - -# 返回值 list[wskey] -def get_wskey(): # 方法 获取 wskey值 [系统变量传递] - if "JD_WSCK" in os.environ: # 判断 JD_WSCK是否存在于环境变量 - wskey_list = os.environ['JD_WSCK'].split('&') # 读取系统变量 以 & 分割变量 - if len(wskey_list) > 0: # 判断 WSKEY 数量 大于 0 个 - return wskey_list # 返回 WSKEY [LIST] - else: # 判断分支 - logger.info("JD_WSCK变量未启用") # 标准日志输出 - sys.exit(1) # 脚本退出 - else: # 判断分支 - logger.info("未添加JD_WSCK变量") # 标准日志输出 - sys.exit(0) # 脚本退出 - - -# 返回值 list[jd_cookie] -def get_ck(): # 方法 获取 JD_COOKIE值 [系统变量传递] - if "JD_COOKIE" in os.environ: # 判断 JD_COOKIE是否存在于环境变量 - ck_list = os.environ['JD_COOKIE'].split('&') # 读取系统变量 以 & 分割变量 - if len(ck_list) > 0: # 判断 WSKEY 数量 大于 0 个 - return ck_list # 返回 JD_COOKIE [LIST] - else: # 判断分支 - logger.info("JD_COOKIE变量未启用") # 标准日志输出 - sys.exit(1) # 脚本退出 - else: # 判断分支 - logger.info("未添加JD_COOKIE变量") # 标准日志输出 - sys.exit(0) # 脚本退出 - - -# 返回值 bool -def check_ck(ck): # 方法 检查 Cookie有效性 使用变量传递 单次调用 - searchObj = re.search(r'pt_pin=([^;\s]+)', ck, re.M | re.I) # 正则检索 pt_pin - if searchObj: # 真值判断 - pin = searchObj.group(1) # 取值 - else: # 判断分支 - pin = ck.split(";")[1] # 取值 使用 ; 分割 - if "WSKEY_UPDATE_HOUR" in os.environ: # 判断 WSKEY_UPDATE_HOUR是否存在于环境变量 - updateHour = 23 # 更新间隔23小时 - if os.environ["WSKEY_UPDATE_HOUR"].isdigit(): # 检查是否为 DEC值 - updateHour = int(os.environ["WSKEY_UPDATE_HOUR"]) # 使用 int化数字 - nowTime = time.time() # 获取时间戳 赋值 - updatedAt = 0.0 # 赋值 - searchObj = re.search(r'__time=([^;\s]+)', ck, re.M | re.I) # 正则检索 [__time=] - if searchObj: # 真值判断 - updatedAt = float(searchObj.group(1)) # 取值 [float]类型 - if nowTime - updatedAt >= (updateHour * 60 * 60) - (10 * 60): # 判断时间操作 - logger.info(str(pin) + ";即将到期或已过期\n") # 标准日志输出 - return False # 返回 Bool类型 False - else: # 判断分支 - remainingTime = (updateHour * 60 * 60) - (nowTime - updatedAt) # 时间运算操作 - hour = int(remainingTime / 60 / 60) # 时间运算操作 [int] - minute = int((remainingTime % 3600) / 60) # 时间运算操作 [int] - logger.info(str(pin) + ";未到期,{0}时{1}分后更新\n".format(hour, minute)) # 标准日志输出 - return True # 返回 Bool类型 True - elif "WSKEY_DISCHECK" in os.environ: # 判断分支 WSKEY_DISCHECK 是否存在于系统变量 - logger.info("不检查账号有效性\n--------------------\n") # 标准日志输出 - return False # 返回 Bool类型 False - else: # 判断分支 - url = 'https://me-api.jd.com/user_new/info/GetJDUserInfoUnion' # 设置JD_API接口地址 - headers = { - 'Cookie': ck, - 'Referer': 'https://home.m.jd.com/myJd/home.action', - 'user-agent': ua - } # 设置 HTTP头 - try: # 异常捕捉 - res = requests.get(url=url, headers=headers, verify=False, timeout=10, allow_redirects=False) # 进行 HTTP请求[GET] 超时 10秒 - except Exception as err: # 异常捕捉 - logger.debug(str(err)) # 调试日志输出 - logger.info("JD接口错误 请重试或者更换IP") # 标准日志输出 - return False # 返回 Bool类型 False - else: # 判断分支 - if res.status_code == 200: # 判断 JD_API 接口是否为 200 [HTTP_OK] - code = int(json.loads(res.text)['retcode']) # 使用 Json模块对返回数据取值 int([retcode]) - if code == 0: # 判断 code值 - logger.info(str(pin) + ";状态正常\n") # 标准日志输出 - return True # 返回 Bool类型 True - else: # 判断分支 - logger.info(str(pin) + ";状态失效\n") - return False # 返回 Bool类型 False - else: # 判断分支 - logger.info("JD接口错误码: " + str(res.status_code)) # 标注日志输出 - return False # 返回 Bool类型 False - - -# 返回值 bool jd_ck -def getToken(wskey): # 方法 获取 Wskey转换使用的 Token 由 JD_API 返回 这里传递 wskey - if flag == 'bak': - return getToken_bak(wskey) - try: # 异常捕捉 - url = str(base64.b64decode(url_t).decode()) + 'api/genToken' # 设置云端服务器地址 路由为 genToken - header = {"User-Agent": ua} # 设置 HTTP头 - params = requests.get(url=url, headers=header, verify=False, timeout=20).json() # 设置 HTTP请求参数 超时 20秒 Json解析 - except Exception as err: # 异常捕捉 - logger.info("Params参数获取失败") # 标准日志输出 - logger.debug(str(err)) # 调试日志输出 - return False, wskey # 返回 -> False[Bool], Wskey - headers = { - 'cookie': wskey, - 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', - 'charset': 'UTF-8', - 'accept-encoding': 'br,gzip,deflate', - 'user-agent': ua - } # 设置 HTTP头 - url = 'https://api.m.jd.com/client.action' # 设置 URL地址 - data = 'body=%7B%22to%22%3A%22https%253a%252f%252fplogin.m.jd.com%252fjd-mlogin%252fstatic%252fhtml%252fappjmp_blank.html%22%7D&' # 设置 POST 载荷 - try: # 异常捕捉 - res = requests.post(url=url, params=params, headers=headers, data=data, verify=False, - timeout=10) # HTTP请求 [POST] 超时 10秒 - res_json = json.loads(res.text) # Json模块 取值 - tokenKey = res_json['tokenKey'] # 取出TokenKey - except Exception as err: # 异常捕捉 - logger.info("JD_WSKEY接口抛出错误 尝试重试 更换IP") # 标准日志输出 - logger.info(str(err)) # 标注日志输出 - return False, wskey # 返回 -> False[Bool], Wskey - else: # 判断分支 - return appjmp(wskey, tokenKey) # 传递 wskey, Tokenkey 执行方法 [appjmp] - -# 备用 -def getToken_bak(wskey): # 方法 获取 Wskey转换使用的 Token 由 JD_API 返回 这里传递 wskey - try: # 异常捕捉 - url = str(base64.b64decode('aHR0cHM6Ly9hcGkubm9sYW5zdG9yZS5jYy8=').decode()) + 'sign' # 设置云端服务器地址 路由为 genToken - header = {"Content-Type": "application/json"} # 设置 HTTP头 - data = {'body':{"to":"https%3a%2f%2fplogin.m.jd.com%2fjd-mlogin%2fstatic%2fhtml%2fappjmp_blank.html"},'fn':'genToken'} - params = requests.post(url=url, headers=header, json=data, verify=False, timeout=20).json() # 设置 HTTP请求参数 超时 20秒 Json解析 - params = 'functionId=genToken&'+params['body'] - except Exception as err: # 异常捕捉 - logger.info("Params参数获取失败") # 标准日志输出 - logger.debug(str(err)) # 调试日志输出 - return False, wskey # 返回 -> False[Bool], Wskey - headers = { - 'cookie': wskey, - 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', - 'charset': 'UTF-8', - 'accept-encoding': 'br,gzip,deflate', - 'user-agent': ua - } # 设置 HTTP头 - url = 'https://api.m.jd.com/client.action' # 设置 URL地址 - data = 'body=%7B%22to%22%3A%22https%253a%252f%252fplogin.m.jd.com%252fjd-mlogin%252fstatic%252fhtml%252fappjmp_blank.html%22%7D&' # 设置 POST 载荷 - try: # 异常捕捉 - res = requests.post(url=url, params=params, headers=headers, data=data, verify=False, - timeout=10) # HTTP请求 [POST] 超时 10秒 - res_json = json.loads(res.text) # Json模块 取值 - tokenKey = res_json['tokenKey'] # 取出TokenKey - except Exception as err: # 异常捕捉 - logger.info("JD_WSKEY接口抛出错误 尝试重试 更换IP") # 标准日志输出 - logger.info(str(err)) # 标注日志输出 - return False, wskey # 返回 -> False[Bool], Wskey - else: # 判断分支 - return appjmp(wskey, tokenKey) # 传递 wskey, Tokenkey 执行方法 [appjmp] - -# 返回值 bool jd_ck -def appjmp(wskey, tokenKey): # 方法 传递 wskey & tokenKey - wskey = "pt_" + str(wskey.split(";")[0]) # 变量组合 使用 ; 分割变量 拼接 pt_ - if tokenKey == 'xxx': # 判断 tokenKey返回值 - logger.info(str(wskey) + ";疑似IP风控等问题 默认为失效\n--------------------\n") # 标准日志输出 - return False, wskey # 返回 -> False[Bool], Wskey - headers = { - 'User-Agent': ua, - 'accept': 'accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', - 'x-requested-with': 'com.jingdong.app.mall' - } # 设置 HTTP头 - params = { - 'tokenKey': tokenKey, - 'to': 'https://plogin.m.jd.com/jd-mlogin/static/html/appjmp_blank.html' - } # 设置 HTTP_URL 参数 - url = 'https://un.m.jd.com/cgi-bin/app/appjmp' # 设置 URL地址 - try: # 异常捕捉 - res = requests.get(url=url, headers=headers, params=params, verify=False, allow_redirects=False, - timeout=20) # HTTP请求 [GET] 阻止跳转 超时 20秒 - except Exception as err: # 异常捕捉 - logger.info("JD_appjmp 接口错误 请重试或者更换IP\n") # 标准日志输出 - logger.info(str(err)) # 标准日志输出 - return False, wskey # 返回 -> False[Bool], Wskey - else: # 判断分支 - try: # 异常捕捉 - res_set = res.cookies.get_dict() # 从res cookie取出 - pt_key = 'pt_key=' + res_set['pt_key'] # 取值 [pt_key] - pt_pin = 'pt_pin=' + res_set['pt_pin'] # 取值 [pt_pin] - if "WSKEY_UPDATE_HOUR" in os.environ: # 判断是否在系统变量中启用 WSKEY_UPDATE_HOUR - jd_ck = str(pt_key) + ';' + str(pt_pin) + ';__time=' + str(time.time()) + ';' # 拼接变量 - else: # 判断分支 - jd_ck = str(pt_key) + ';' + str(pt_pin) + ';' # 拼接变量 - except Exception as err: # 异常捕捉 - logger.info("JD_appjmp提取Cookie错误 请重试或者更换IP\n") # 标准日志输出 - logger.info(str(err)) # 标准日志输出 - return False, wskey # 返回 -> False[Bool], Wskey - else: # 判断分支 - if 'fake' in pt_key: # 判断 pt_key中 是否存在fake - logger.info(str(wskey) + ";WsKey状态失效\n") # 标准日志输出 - return False, wskey # 返回 -> False[Bool], Wskey - else: # 判断分支 - logger.info(str(wskey) + ";WsKey状态正常\n") # 标准日志输出 - return True, jd_ck # 返回 -> True[Bool], jd_ck - - -def update(): # 方法 脚本更新模块 - up_ver = int(cloud_arg['update']) # 云端参数取值 [int] - if ver >= up_ver: # 判断版本号大小 - logger.info("当前脚本版本: " + str(ver)) # 标准日志输出 - logger.info("--------------------\n") # 标准日志输出 - else: # 判断分支 - logger.info("当前脚本版本: " + str(ver) + "新版本: " + str(up_ver)) # 标准日志输出 - logger.info("存在新版本, 请更新脚本后执行") # 标准日志输出 - logger.info("--------------------\n") # 标准日志输出 - text = '当前脚本版本: {0}新版本: {1}, 请更新脚本~!'.format(ver, up_ver) # 设置发送内容 - ql_send(text) - # sys.exit(0) # 退出脚本 [未启用] - - -def ql_check(port): # 方法 检查青龙端口 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Socket模块初始化 - sock.settimeout(2) # 设置端口超时 - try: # 异常捕捉 - sock.connect(('127.0.0.1', port)) # 请求端口 - except Exception as err: # 捕捉异常 - logger.debug(str(err)) # 调试日志输出 - sock.close() # 端口关闭 - return False # 返回 -> False[Bool] - else: # 分支判断 - sock.close() # 关闭端口 - return True # 返回 -> True[Bool] - - -def serch_ck(pin): # 方法 搜索 Pin - for i in range(len(envlist)): # For循环 变量[envlist]的数量 - if "name" not in envlist[i] or envlist[i]["name"] != "JD_COOKIE": # 判断 envlist内容 - continue # 继续循环 - if pin in envlist[i]['value']: # 判断envlist取值['value'] - value = envlist[i]['value'] # 取值['value'] - id = envlist[i][ql_id] # 取值 [ql_id](变量) - logger.info(str(pin) + "检索成功\n") # 标准日志输出 - return True, value, id # 返回 -> True[Bool], value, id - else: # 判断分支 - continue # 继续循环 - logger.info(str(pin) + "检索失败\n") # 标准日志输出 - return False, 1 # 返回 -> False[Bool], 1 - - -def get_env(): # 方法 读取变量 - url = ql_url + 'api/envs' - try: # 异常捕捉 - res = s.get(url) # HTTP请求 [GET] 使用 session - except Exception as err: # 异常捕捉 - logger.debug(str(err)) # 调试日志输出 - logger.info("\n青龙环境接口错误") # 标准日志输出 - sys.exit(1) # 脚本退出 - else: # 判断分支 - data = json.loads(res.text)['data'] # 使用Json模块提取值[data] - return data # 返回 -> data - - -def check_id(): # 方法 兼容青龙老版本与新版本 id & _id的问题 - url = ql_url + 'api/envs' - try: # 异常捕捉 - res = s.get(url).json() # HTTP[GET] 请求 使用 session - except Exception as err: # 异常捕捉 - logger.debug(str(err)) # 调试日志输出 - logger.info("\n青龙环境接口错误") # 标准日志输出 - sys.exit(1) # 脚本退出 - else: # 判断分支 - if '_id' in res['data'][0]: # 判断 [_id] - logger.info("使用 _id 键值") # 标准日志输出 - return '_id' # 返回 -> '_id' - else: # 判断分支 - logger.info("使用 id 键值") # 标准日志输出 - return 'id' # 返回 -> 'id' - - -def ql_update(e_id, n_ck): # 方法 青龙更新变量 传递 id cookie - url = ql_url + 'api/envs' - data = { - "name": "JD_COOKIE", - "value": n_ck, - ql_id: e_id - } # 设置 HTTP POST 载荷 - data = json.dumps(data) # json模块格式化 - s.put(url=url, data=data) # HTTP [PUT] 请求 使用 session - ql_enable(eid) # 调用方法 ql_enable 传递 eid - - -def ql_enable(e_id): # 方法 青龙变量启用 传递值 eid - url = ql_url + 'api/envs/enable' - data = '["{0}"]'.format(e_id) # 格式化 POST 载荷 - res = json.loads(s.put(url=url, data=data).text) # json模块读取 HTTP[PUT] 的返回值 - if res['code'] == 200: # 判断返回值为 200 - logger.info("\n账号启用\n--------------------\n") # 标准日志输出 - return True # 返回 ->True - else: # 判断分支 - logger.info("\n账号启用失败\n--------------------\n") # 标准日志输出 - return False # 返回 -> Fasle - - -def ql_disable(e_id): # 方法 青龙变量禁用 传递 eid - url = ql_url + 'api/envs/disable' - data = '["{0}"]'.format(e_id) # 格式化 POST 载荷 - res = json.loads(s.put(url=url, data=data).text) # json模块读取 HTTP[PUT] 的返回值 - if res['code'] == 200: # 判断返回值为 200 - logger.info("\n账号禁用成功\n--------------------\n") # 标准日志输出 - return True # 返回 ->True - else: # 判断分支 - logger.info("\n账号禁用失败\n--------------------\n") # 标准日志输出 - return False # 返回 -> Fasle - - -def ql_insert(i_ck): # 方法 插入新变量 - data = [{"value": i_ck, "name": "JD_COOKIE"}] # POST数据载荷组合 - data = json.dumps(data) # Json格式化数据 - url = ql_url + 'api/envs' - s.post(url=url, data=data) # HTTP[POST]请求 使用session - logger.info("\n账号添加完成\n--------------------\n") # 标准日志输出 - - -def cloud_info(): # 方法 云端信息 - url = str(base64.b64decode(url_t).decode()) + 'api/check_api' # 设置 URL地址 路由 [check_api] - for i in range(3): # For循环 3次 - try: # 异常捕捉 - headers = {"authorization": "Bearer Shizuku"} # 设置 HTTP头 - res = requests.get(url=url, verify=False, headers=headers, timeout=20).text # HTTP[GET] 请求 超时 20秒 - except requests.exceptions.ConnectTimeout: # 异常捕捉 - logger.info("\n获取云端参数超时, 正在重试!" + str(i)) # 标准日志输出 - time.sleep(1) # 休眠 1秒 - continue # 循环继续 - except requests.exceptions.ReadTimeout: # 异常捕捉 - logger.info("\n获取云端参数超时, 正在重试!" + str(i)) # 标准日志输出 - time.sleep(1) # 休眠 1秒 - continue # 循环继续 - except Exception as err: # 异常捕捉 - logger.info("\n未知错误云端, 退出脚本!") # 标准日志输出 - logger.debug(str(err)) # 调试日志输出 - sys.exit(1) # 脚本退出 - else: # 分支判断 - try: # 异常捕捉 - c_info = json.loads(res) # json读取参数 - except Exception as err: # 异常捕捉 - logger.info("云端参数解析失败") # 标准日志输出 - logger.debug(str(err)) # 调试日志输出 - sys.exit(1) # 脚本退出 - else: # 分支判断 - return c_info # 返回 -> c_info - - -def check_cloud(): # 方法 云端地址检查 - url_list = ['aHR0cHM6Ly9hcGkubW9tb2UubWwv', 'aHR0cHM6Ly9hcGkubGltb2UuZXUub3JnLw==', 'aHR0cHM6Ly9hcGkuaWxpeWEuY2Yv'] # URL list Encode - for i in url_list: # for循环 url_list - url = str(base64.b64decode(i).decode()) # 设置 url地址 [str] - try: # 异常捕捉 - requests.get(url=url, verify=False, timeout=10) # HTTP[GET]请求 超时 10秒 - except Exception as err: # 异常捕捉 - logger.debug(str(err)) # 调试日志输出 - continue # 循环继续 - else: # 分支判断 - info = ['HTTPS', 'Eu_HTTPS', 'CloudFlare'] # 输出信息[List] - logger.info(str(info[url_list.index(i)]) + " Server Check OK\n--------------------\n") # 标准日志输出 - return i # 返回 ->i - logger.info("\n云端地址全部失效, 请检查网络!") # 标准日志输出 - #ql_send('云端地址失效. 请联系作者或者检查网络.') # 推送消息 - return 403 - #sys.exit(1) # 脚本退出 - - -def check_port(): # 方法 检查变量传递端口 - logger.info("\n--------------------\n") # 标准日志输出 - if "QL_PORT" in os.environ: # 判断 系统变量是否存在[QL_PORT] - try: # 异常捕捉 - port = int(os.environ['QL_PORT']) # 取值 [int] - except Exception as err: # 异常捕捉 - logger.debug(str(err)) # 调试日志输出 - logger.info("变量格式有问题...\n格式: export QL_PORT=\"端口号\"") # 标准日志输出 - logger.info("使用默认端口5700") # 标准日志输出 - return 5700 # 返回端口 5700 - else: # 判断分支 - port = 5700 # 默认5700端口 - if not ql_check(port): # 调用方法 [ql_check] 传递 [port] - logger.info(str(port) + "端口检查失败, 如果改过端口, 请在变量中声明端口 \n在config.sh中加入 export QL_PORT=\"端口号\"") # 标准日志输出 - logger.info("\n如果你很确定端口没错, 还是无法执行, 在GitHub给我发issus\n--------------------\n") # 标准日志输出 - sys.exit(1) # 脚本退出 - else: # 判断分支 - logger.info(str(port) + "端口检查通过") # 标准日志输出 - return port # 返回->port - - -if __name__ == '__main__': # Python主函数执行入口 - port = check_port() # 调用方法 [check_port] 并赋值 [port] - ql_url = 'http://127.0.0.1:{0}/'.format(port) - token = ql_login() # 调用方法 [ql_login] 并赋值 [token] - s = requests.session() # 设置 request session方法 - s.headers.update({"authorization": "Bearer " + str(token)}) # 增加 HTTP头认证 - s.headers.update({"Content-Type": "application/json;charset=UTF-8"}) # 增加 HTTP头 json 类型 - ql_id = check_id() # 调用方法 [check_id] 并赋值 [ql_id] - url_t = check_cloud() # 调用方法 [check_cloud] 并赋值 [url_t] - flag = '' - if url_t == 403: - logger.info("\n尝试使用nolan接口请求\n") - ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' - flag = 'bak' - else: - cloud_arg = cloud_info() # 调用方法 [cloud_info] 并赋值 [cloud_arg] - update() # 调用方法 [update] - ua = cloud_arg['User-Agent'] # 设置全局变量 UA - wslist = get_wskey() # 调用方法 [get_wskey] 并赋值 [wslist] - envlist = get_env() # 调用方法 [get_env] 并赋值 [envlist] - if "WSKEY_SLEEP" in os.environ and str(os.environ["WSKEY_SLEEP"]).isdigit(): # 判断变量[WSKEY_SLEEP]是否为数字类型 - sleepTime = int(os.environ["WSKEY_SLEEP"]) # 获取变量 [int] - else: # 判断分支 - sleepTime = 10 # 默认休眠时间 10秒 - for ws in wslist: # wslist变量 for循环 [wslist -> ws] - wspin = ws.split(";")[0] # 变量分割 ; - if "pin" in wspin: # 判断 pin 是否存在于 [wspin] - wspin = "pt_" + wspin + ";" # 封闭变量 - return_serch = serch_ck(wspin) # 变量 pt_pin 搜索获取 key eid - if return_serch[0]: # bool: True 搜索到账号 - jck = str(return_serch[1]) # 拿到 JD_COOKIE - if not check_ck(jck): # bool: False 判定 JD_COOKIE 有效性 - tryCount = 1 # 重试次数 1次 - if "WSKEY_TRY_COUNT" in os.environ: # 判断 [WSKEY_TRY_COUNT] 是否存在于系统变量 - if os.environ["WSKEY_TRY_COUNT"].isdigit(): # 判断 [WSKEY_TRY_COUNT] 是否为数字 - tryCount = int(os.environ["WSKEY_TRY_COUNT"]) # 设置 [tryCount] int - for count in range(tryCount): # for循环 [tryCount] - count += 1 # 自增 - return_ws = getToken(ws) # 使用 WSKEY 请求获取 JD_COOKIE bool jd_ck - if return_ws[0]: # 判断 [return_ws]返回值 Bool类型 - break # 中断循环 - if count < tryCount: # 判断循环次 - logger.info("{0} 秒后重试,剩余次数:{1}\n".format(sleepTime, tryCount - count)) # 标准日志输出 - time.sleep(sleepTime) # 脚本休眠 使用变量 [sleepTime] - if return_ws[0]: # 判断 [return_ws]返回值 Bool类型 - nt_key = str(return_ws[1]) # 从 return_ws[1] 取出 -> nt_key - # logger.info("wskey转pt_key成功", nt_key) # 标准日志输出 [未启用] - logger.info("wskey转换成功") # 标准日志输出 - eid = return_serch[2] # 从 return_serch 拿到 eid - ql_update(eid, nt_key) # 函数 ql_update 参数 eid JD_COOKIE - else: # 判断分支 - if "WSKEY_AUTO_DISABLE" in os.environ: # 从系统变量中获取 WSKEY_AUTO_DISABLE - logger.info(str(wspin) + "账号失效") # 标准日志输出 - text = "账号: {0} WsKey疑似失效".format(wspin) # 设置推送内容 - else: # 判断分支 - eid = return_serch[2] # 读取 return_serch[2] -> eid - logger.info(str(wspin) + "账号禁用") # 标准日志输出 - ql_disable(eid) # 执行方法[ql_disable] 传递 eid - text = "账号: {0} WsKey疑似失效, 已禁用Cookie".format(wspin) # 设置推送内容 - ql_send(text) - else: # 判断分支 - logger.info(str(wspin) + "账号有效") # 标准日志输出 - eid = return_serch[2] # 读取 return_serch[2] -> eid - ql_enable(eid) # 执行方法[ql_enable] 传递 eid - logger.info("--------------------\n") # 标准日志输出 - else: # 判断分支 - logger.info("\n新wskey\n") # 标准日志分支 - return_ws = getToken(ws) # 使用 WSKEY 请求获取 JD_COOKIE bool jd_ck - if return_ws[0]: # 判断 (return_ws[0]) 类型: [Bool] - nt_key = str(return_ws[1]) # return_ws[1] -> nt_key - logger.info("wskey转换成功\n") # 标准日志输出 - ql_insert(nt_key) # 调用方法 [ql_insert] - logger.info("暂停{0}秒\n".format(sleepTime)) # 标准日志输出 - time.sleep(sleepTime) # 脚本休眠 - else: # 判断分支 - logger.info("WSKEY格式错误\n--------------------\n") # 标准日志输出 - logger.info("执行完成\n--------------------") # 标准日志输出 - sys.exit(0) # 脚本退出 - # Enjoy