├── .github └── workflows │ ├── additional.yml │ ├── frequently.yml │ └── qqread.yml ├── README.md ├── notification.py ├── qqread.py ├── qqreadCookie.py └── requirements.txt /.github/workflows/additional.yml: -------------------------------------------------------------------------------- 1 | name: additional 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '*/10 * * * ?' 7 | watch: 8 | types: [started] 9 | env: 10 | TZ: Asia/Shanghai 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | if: github.event.repository.owner.id == github.event.sender.id 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: 'Set up Python' 20 | uses: actions/setup-python@v1 21 | with: 22 | python-version: 3.8 23 | 24 | - name: 'Install requirements' 25 | run: pip install -r ./requirements.txt 26 | 27 | - name: '运行【additional】' 28 | run: python3 qqread.py 29 | env: 30 | QQREADHEADERS: ${{ secrets.QQREADHEADERS }} 31 | QQREADBODYS: ${{ secrets.QQREADBODYS }} 32 | QQREADTIMEURL: ${{ secrets.QQREADTIMEURL }} 33 | NOTIFYTYPE: ${{ secrets.NOTIFYTYPE }} 34 | NOTIFYCFG: ${{ secrets.NOTIFYCFG }} 35 | DRAWAMOUNT: ${{ secrets.DRAWAMOUNT }} 36 | SCKEY: ${{ secrets.SCKEY }} 37 | BARK: ${{ secrets.BARK }} 38 | TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }} 39 | TG_USER_ID: ${{ secrets.TG_USER_ID }} 40 | -------------------------------------------------------------------------------- /.github/workflows/frequently.yml: -------------------------------------------------------------------------------- 1 | name: frequently 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '*/4 * * * ?' 7 | watch: 8 | types: [started] 9 | env: 10 | TZ: Asia/Shanghai 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | if: github.event.repository.owner.id == github.event.sender.id 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: 'Set up Python' 20 | uses: actions/setup-python@v1 21 | with: 22 | python-version: 3.8 23 | 24 | - name: 'Install requirements' 25 | run: pip install -r ./requirements.txt 26 | 27 | - name: '运行【frequently】' 28 | run: python3 qqread.py 29 | env: 30 | QQREADHEADERS: ${{ secrets.QQREADHEADERS }} 31 | QQREADBODYS: ${{ secrets.QQREADBODYS }} 32 | QQREADTIMEURL: ${{ secrets.QQREADTIMEURL }} 33 | NOTIFYTYPE: ${{ secrets.NOTIFYTYPE }} 34 | NOTIFYCFG: ${{ secrets.NOTIFYCFG }} 35 | DRAWAMOUNT: ${{ secrets.DRAWAMOUNT }} 36 | SCKEY: ${{ secrets.SCKEY }} 37 | BARK: ${{ secrets.BARK }} 38 | TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }} 39 | TG_USER_ID: ${{ secrets.TG_USER_ID }} 40 | -------------------------------------------------------------------------------- /.github/workflows/qqread.yml: -------------------------------------------------------------------------------- 1 | name: 企鹅读书 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0,5,10,15,20,25,30,35,40,45,50,55 * * * *' 7 | watch: 8 | types: [started] 9 | env: 10 | TZ: Asia/Shanghai 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | if: github.event.repository.owner.id == github.event.sender.id 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: 'Set up Python' 20 | uses: actions/setup-python@v1 21 | with: 22 | python-version: 3.8 23 | 24 | - name: 'Install requirements' 25 | run: pip install -r ./requirements.txt 26 | 27 | - name: '运行【企鹅读书】' 28 | run: python3 qqread.py 29 | env: 30 | QQREADHEADERS: ${{ secrets.QQREADHEADERS }} 31 | QQREADBODYS: ${{ secrets.QQREADBODYS }} 32 | QQREADTIMEURL: ${{ secrets.QQREADTIMEURL }} 33 | NOTIFYTYPE: ${{ secrets.NOTIFYTYPE }} 34 | NOTIFYCFG: ${{ secrets.NOTIFYCFG }} 35 | DRAWAMOUNT: ${{ secrets.DRAWAMOUNT }} 36 | SCKEY: ${{ secrets.SCKEY }} 37 | BARK: ${{ secrets.BARK }} 38 | TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }} 39 | TG_USER_ID: ${{ secrets.TG_USER_ID }} 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QQRead使用说明 2 | 3 | > 基于python的自动化脚本 4 | > 低调使用,请勿到处宣传 5 | 6 | 7 | ### 特别声明: 8 | 9 | - 本仓库发布的脚本及其中涉及的任何解锁和解密分析脚本,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。 10 | 11 | - 本项目内所有资源文件,禁止任何公众号、自媒体进行任何形式的转载、发布。 12 | 13 | - 本人对任何脚本问题概不负责,包括但不限于由任何脚本错误导致的任何损失或损害。 14 | 15 | - 间接使用脚本的任何用户,包括但不限于建立VPS或在某些行为违反国家/地区法律或相关法规的情况下进行传播, 本人对于由此引起的任何隐私泄漏或其他后果概不负责。 16 | 17 | - 请勿将本仓库的任何内容用于商业或非法目的,否则后果自负。 18 | 19 | - 如果任何单位或个人认为该项目的脚本可能涉嫌侵犯其权利,则应及时通知并提供身份证明,所有权证明,我们将在收到认证文件后删除相关脚本。 20 | 21 | - 任何以任何方式查看此项目的人或直接或间接使用该项目的任何脚本的使用者都应仔细阅读此声明。本人保留随时更改或补充此免责声明的权利。一旦使用并复制了任何相关脚本或Script项目的规则,则视为您已接受此免责声明。 22 | 23 | **您必须在下载后的24小时内从计算机或手机中完全删除以上内容** 24 | > ***您使用或者复制了本仓库且本人制作的任何脚本,则视为 `已接受` 此声明,请仔细阅读*** 25 | 26 | 27 | 28 | ### 主要参数: 29 | 30 | | 名称 | 功能 | 属性 | 备注 | 31 | | :------------------: | :-----------: | :----: | ------------------------------ | 32 | | `QQREADHEADERS` | 主header | 必须 | 绝大多数功能的正常使用需要此参数 | 33 | | `QQREADBODYS` | 主body | 必须 | 绝大多数功能的正常使用需要此参数 | 34 | | `QQREADTIMEURL` | 阅读时长URL | 必须 | 上传阅读时长功能需要的URL | 35 | | `NOTIFYTYPE` | 通知类型 | 非必须 | 详见通知类型 | 36 | | `NOTIFYCFG` | 通知服务 | 非必须 | 详见通知服务 | 37 | | `SCKEY` | server酱key | 非必须 | 自行获取 | 38 | | `BARK` | bark秘钥 | 非必须 | 自行获取 | 39 | | `TG_BOT_TOKEN` | telegram推送 | 非必须 | tg推送,填写自己申请[@BotFather](https://t.me/BotFather)的Token,如`10xxx4:AAFcqxxxxgER5uw` , [具体教程](https://github.com/lxk0301/jd_scripts/blob/master/backUp/TG_PUSH.md) | 40 | | `TG_USER_ID` | telegram推送 | 非必须 | tg推送,填写[@getuseridbot](https://t.me/getuseridbot)中获取到的纯数字ID, [具体教程](https://github.com/lxk0301/jd_scripts/blob/master/backUp/TG_PUSH.md) | 41 | 42 | 43 | **⚠️cookie获取方法:** 44 | 45 | 1. 进入 https://m.q.qq.com/a/s/d3eacc70120b9a37e46bad408c0c4c2a 46 | 47 | 2. 进一本书阅读一会儿,然后退出,获取`QQREADHEADERS` `QQREADBODYS` 和 `QQREADTIMEURL` 48 | 49 | 3. `QQREADHEADERS` 和 `QQREADTIMEURL` 匹配链接为 https://mqqapi.reader.qq.com/mqq/addReadTimeWithBid?....... 50 | 51 | `QQREADBODYS` 匹配链接为 https://mqqapi.reader.qq.com/log/v4/mqq/track 52 | 53 | 4. `QQREADHEADERS`参数格式为 54 | 55 | 56 | ``` 57 | {"Cookie":"ywguid=123456789;ywkey=wedqwdlAWVJp9;platform=android;channel=mqqmina;mpVersion=0.30.0;qq_ver=8.4.18.4945;os_ver=Android10","aaa":"bbb",......} 58 | ``` 59 | 60 | 多账号请按`Enter`键换行隔开示例(这里给下三个账号的示例) 61 | 62 | ``` 63 | {"Cookie":"ywguid=123456789;ywkey=******","aaa":"bbb",......} 64 | {"Cookie":"ywguid=123456789;ywkey=******","aaa":"bbb",......} 65 | {"Cookie":"ywguid=123456789;ywkey=******","aaa":"bbb",......} 66 | ``` 67 | 68 | 5. `QQREADBODYS` 参数格式为 69 | 70 | ``` 71 | {"common":{"appid":***,"areaid":5,"qq_ver":"8.4.18.4945","os_ver":"Android 10","mp_ver":"0.31.0","mpos_ver":"1.21.0","brand":"***","model":"***","screenWidth":393,"screenHeight":816,"windowWidth":393,"windowHeight":762,"openid":"***","guid":***,"session":"***","scene":1023,"source":-1,"hasRedDot":"false","missions":-1,"caseID":-1},"dataList":[{"click1":"bookShelf_myshelf_myBook_C","click2":"qqauthorize_addRCS_succ_C","route":"pages/book-read/index","refer":"pages/book-shelf/index","options":{"bid":"27325720","cid":"3","from":"shelf"},"dis":1607325831391,"ext6":31,"eventID":"bookRead_show_I","type":"shown","ccid":3,"bid":"27325720","bookStatus":1,"bookPay":1,"chapterStatus":0,"ext1":{"font":18,"bg":0,"pageMode":1},"from":"bookShelf_myshelf_myBook_C_0_27325720"}]} 72 | ``` 73 | 74 | 多账号请按`Enter`键换行隔开示例(这里给下三个账号的示例) 75 | 76 | ``` 77 | {"common":{"appid":***,"areaid":5,"qq_ver":"8.4.18.4945","os_ver":"Android 10","mp_ver":"0.31.0","mpos_ver":"1.21.0","brand":"***","model":"***","screenWidth":393,"screenHeight":816,"windowWidth":393,"windowHeight":762,"openid":"***","guid":***,"session":"***","scene":1023,"source":-1,"hasRedDot":"false","missions":-1,"caseID":-1},"dataList":[{"click1":"bookShelf_myshelf_myBook_C","click2":"qqauthorize_addRCS_succ_C","route":"pages/book-read/index","refer":"pages/book-shelf/index","options":{"bid":"27325720","cid":"3","from":"shelf"},"dis":1607325831391,"ext6":31,"eventID":"bookRead_show_I","type":"shown","ccid":3,"bid":"27325720","bookStatus":1,"bookPay":1,"chapterStatus":0,"ext1":{"font":18,"bg":0,"pageMode":1},"from":"bookShelf_myshelf_myBook_C_0_27325720"}]} 78 | {"common":{"appid":***,"areaid":5,"qq_ver":"8.4.18.4945","os_ver":"Android 10","mp_ver":"0.31.0","mpos_ver":"1.21.0","brand":"***","model":"***","screenWidth":393,"screenHeight":816,"windowWidth":393,"windowHeight":762,"openid":"***","guid":***,"session":"***","scene":1023,"source":-1,"hasRedDot":"false","missions":-1,"caseID":-1},"dataList":[{"click1":"bookShelf_myshelf_myBook_C","click2":"qqauthorize_addRCS_succ_C","route":"pages/book-read/index","refer":"pages/book-shelf/index","options":{"bid":"27325720","cid":"3","from":"shelf"},"dis":1607325831391,"ext6":31,"eventID":"bookRead_show_I","type":"shown","ccid":3,"bid":"27325720","bookStatus":1,"bookPay":1,"chapterStatus":0,"ext1":{"font":18,"bg":0,"pageMode":1},"from":"bookShelf_myshelf_myBook_C_0_27325720"}]} 79 | {"common":{"appid":***,"areaid":5,"qq_ver":"8.4.18.4945","os_ver":"Android 10","mp_ver":"0.31.0","mpos_ver":"1.21.0","brand":"***","model":"***","screenWidth":393,"screenHeight":816,"windowWidth":393,"windowHeight":762,"openid":"***","guid":***,"session":"***","scene":1023,"source":-1,"hasRedDot":"false","missions":-1,"caseID":-1},"dataList":[{"click1":"bookShelf_myshelf_myBook_C","click2":"qqauthorize_addRCS_succ_C","route":"pages/book-read/index","refer":"pages/book-shelf/index","options":{"bid":"27325720","cid":"3","from":"shelf"},"dis":1607325831391,"ext6":31,"eventID":"bookRead_show_I","type":"shown","ccid":3,"bid":"27325720","bookStatus":1,"bookPay":1,"chapterStatus":0,"ext1":{"font":18,"bg":0,"pageMode":1},"from":"bookShelf_myshelf_myBook_C_0_27325720"}]} 80 | ``` 81 | 82 | 6. `QQREADTIMEURL` 参数格式为 83 | 84 | ``` 85 | https://mqqapi.reader.qq.com/mqq/addReadTimeWithBid?scene=***&refer=-1&bid=***&readTime=***&read_type=0&conttype=1&read_status=0&chapter_info=%5B%7B%221%22%3A%7B%22readTime%22%3A***%2C%22pay_status%22%3A0%7D%7D%5D&sp=-1 86 | ``` 87 | 88 | 多账号请按`Enter`键换行隔开示例(这里给下三个账号的示例) 89 | 90 | ``` 91 | https://mqqapi.reader.qq.com/mqq/addReadTimeWithBid?scene=***&refer=-1&bid=***&readTime=***&read_type=0&conttype=1&read_status=0&chapter_info=%5B%7B%221%22%3A%7B%22readTime%22%3A***%2C%22pay_status%22%3A0%7D%7D%5D&sp=-1 92 | https://mqqapi.reader.qq.com/mqq/addReadTimeWithBid?scene=***&refer=-1&bid=***&readTime=***&read_type=0&conttype=1&read_status=0&chapter_info=%5B%7B%221%22%3A%7B%22readTime%22%3A***%2C%22pay_status%22%3A0%7D%7D%5D&sp=-1 93 | https://mqqapi.reader.qq.com/mqq/addReadTimeWithBid?scene=***&refer=-1&bid=***&readTime=***&read_type=0&conttype=1&read_status=0&chapter_info=%5B%7B%221%22%3A%7B%22readTime%22%3A***%2C%22pay_status%22%3A0%7D%7D%5D&sp=-1 94 | ``` 95 | 96 | 7. **特别注意:** 三个参数中每个账号信息出现的顺序一定要一致,且每个参数有几个账号就写几行,不要有多余空行! 97 | 98 | ### 运行方式 99 | 100 | ##### 1、方案一 101 | 102 | 本地执行、云服务器、云函数等等 103 | 104 | 下载到本地,填写 `qqreadCookie.py` 中的 `qqreadheaders` `qqreadbodys` `qqreadtimeurl` 等信息 105 | 云函数请善用搜索以及对应官方文档 106 | 107 | ##### 2、方案二 108 | 109 | GitHub action自动运行,账号信息读取自 `Repo-Setting-Secrets` 110 | **!请勿滥用!** 111 | 112 | 113 | 114 | ### 通知服务 115 | 116 | 默认不开启,需要通知服务的需修改 `NOTIFYCFG` 参数 117 | 118 | 目前会发送通知的情况有: 账号headers过期; 全部通知;收获宝箱通知;每收获15个宝箱通知 119 | 120 | 支持两种通知方式,后续看心情添加其他通知方式 121 | 122 | ``` 123 | [0,1,2,3] 0:不通知 1:server酱 2:bark服务 3:Telegram_bot 124 | ``` 125 | 126 | 1. 使用Server酱的需要参数 `SCKEY` ,支持GitHub action 127 | 2. 使用bark的填写 `BARK` ,支持GitHub action 128 | 3. 使用Telegram Bot的填写 `TG_BOT_TOKEN` `TG_USER_ID` ,支持GitHub action 129 | 130 | ### 通知类型 131 | 132 | 默认为每领15个宝箱通知一次,需要其他通知类型请修改 `NOTIFYTYPE` 参数 133 | 134 | 支持三种通知类型 135 | 136 | ``` 137 | [0,1,2,3] 0:关闭通知 1:所有通知 2:领取宝箱成功通知 3:每领15个宝箱通知一次 138 | ``` 139 | 140 | ### 同步Fork后的代码 141 | 142 | 手动同步,[具体教程](http://www.ibloger.net/article/3361.html) 143 | 144 | ### 特别感谢(排名不分先后): 145 | 146 | * [@ziye12](https://github.com/ziye12) 147 | 148 | * [@Zero-S1](https://github.com/Zero-S1) 149 | 150 | * [@lxk0301](https://github.com/lxk0301) 151 | 152 | -------------------------------------------------------------------------------- /notification.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import os 3 | import ast 4 | import time 5 | 6 | 7 | ####################### 8 | # 通知服务 9 | ####################### 10 | 11 | 12 | 13 | NOTIFYCFG = 0 # [0,1,2,3] 0:不通知 1:server酱 2:bark服务 3:Telegram_bot 14 | 15 | SCKEY = '' # Server酱的SCKEY 16 | BARK = '' # bark服务,自行搜索 17 | TG_BOT_TOKEN = '' # telegram bot token 自行申请 18 | TG_USER_ID = '' # telegram 用户ID 19 | ################################################################## 20 | 21 | 22 | def n0(a, b): 23 | """空函数,即不使用通知服务""" 24 | print(">>>>未开启通知服务") 25 | return 26 | 27 | 28 | def serverJ(title, content): 29 | """server酱服务""" 30 | sckey = SCKEY 31 | if "SCKEY" in os.environ: 32 | """ 33 | 判断是否运行自GitHub action,"SCKEY" 该参数与 repo里的Secrets的名称保持一致 34 | """ 35 | sckey = os.environ["SCKEY"] 36 | 37 | if not sckey: 38 | print("server酱服务的SCKEY未设置!!\n取消推送") 39 | return 40 | print("serverJ服务启动") 41 | content = content.replace("\n", "\n\n") 42 | data = { 43 | "text": title, 44 | "desp": content 45 | } 46 | response = requests.post(f"https://sc.ftqq.com/{sckey}.send", data=data) 47 | print(response.text) 48 | 49 | 50 | def bark(title, content): 51 | """bark服务""" 52 | bark_token = BARK 53 | title = content.replace("#", "") 54 | content = content.replace("#", "") 55 | if "BARK" in os.environ: 56 | """ 57 | 判断是否运行自GitHub action,"BARK" 该参数与 repo里的Secrets的名称保持一致 58 | """ 59 | bark_token = os.environ["BARK"] 60 | if not bark_token: 61 | print("bark服务的bark_token未设置!!\n取消推送") 62 | return 63 | response = requests.get( 64 | f"""https://api.day.app/{bark_token}/{title}/{content}""") 65 | print(response.text) 66 | 67 | 68 | def telegram_bot(title, content): 69 | """Telegram_bot服务""" 70 | print("\n") 71 | tg_bot_token = TG_BOT_TOKEN 72 | tg_user_id = TG_USER_ID 73 | if "TG_BOT_TOKEN" in os.environ and "TG_USER_ID" in os.environ: 74 | tg_bot_token = os.environ["TG_BOT_TOKEN"] 75 | tg_user_id = os.environ["TG_USER_ID"] 76 | if not tg_bot_token or not tg_user_id: 77 | print("Telegram推送的tg_bot_token或者tg_user_id未设置!!\n取消推送") 78 | return 79 | print("Telegram 推送开始") 80 | send_data = {"chat_id": tg_user_id, "text": title + 81 | '\n\n'+content, "disable_web_page_preview": "true"} 82 | response = requests.post( 83 | url=f'https://api.telegram.org/bot{tg_bot_token}/sendMessage', data=send_data) 84 | print(response.json()['ok']) 85 | 86 | 87 | if "NOTIFYCFG" in os.environ and os.environ["NOTIFYCFG"].strip(): 88 | NOTIFYCFG = ast.literal_eval(os.environ["NOTIFYCFG"]) 89 | 90 | notify = [n0, serverJ, bark, telegram_bot][NOTIFYCFG] 91 | 92 | if __name__ == "__main__": 93 | print("通知服务测试") 94 | start = time.time() 95 | notify("QQRead脚本通知服务", "needYou2Know\n通知服务测试") 96 | print("耗时: ", time.time()-start, "s") 97 | -------------------------------------------------------------------------------- /qqread.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2020-12-18 3 | # @Author : water008@github 4 | # @File : qqread.py 5 | 6 | """ 7 | 8 | Github地址 https://github.com/Water008/qqread 9 | TG 频道 https://t.me/water_scripts 10 | TG 群组 https://t.me/joinchat/AAAAAEhFTR9JV3Vf6NAWZw 11 | 12 | ⚠️必须参数获取方法: 13 | 14 | 进入 https://m.q.qq.com/a/s/6fb00f7035f82425df91a5b668f6be8b 15 | 16 | 进一本书阅读一会儿,然后退出,获取QQREADHEADERS QQREADBODYS 和 QQREADTIMEURL 17 | 18 | QQREADHEADERS 和 QQREADTIMEURL 匹配链接为 https://mqqapi.reader.qq.com/mqq/addReadTimeWithBid?....... 19 | 20 | QQREADBODYS 匹配链接为 https://mqqapi.reader.qq.com/log/v4/mqq/track 21 | 22 | 详细说明请阅读 https://github.com/Water008/qqread/blob/main/README.md 23 | 24 | 如遇问题,欢迎提交Issues或在TG反馈 25 | 26 | ⚠️宝箱奖励为20分钟一次,自己根据情况设置定时,建议cron设置为 */11 * * * * ,即每11分钟运行一次 27 | 28 | """ 29 | 30 | import os 31 | import re 32 | import ast 33 | import time 34 | import json 35 | import random 36 | import requests 37 | import qqreadCookie 38 | import notification 39 | from datetime import datetime, timedelta 40 | 41 | 42 | # 以下为可修改参数 43 | TIME = 5 # 单次上传阅读时间,默认为5分钟 44 | LIMIT_TIME = 18 # 每日最大上传阅读时间,默认为18小时 45 | DELAYSEC = 1 # 单次任务延时,默认为1秒 46 | NOTIFYTYPE = 3 # 0为关闭通知,1为所有通知,2为领取宝箱成功通知,3为每领15个宝箱通知一次 47 | DRAWAMOUNT = 0 # [0, 10, 30, 50, 100] 分别为关闭自动提现、提现10元、30元、50元、100元,默认为关闭 48 | # 以上为可修改参数 49 | 50 | if "NOTIFYTYPE" in os.environ and os.environ["NOTIFYTYPE"].strip(): 51 | NOTIFYTYPE = ast.literal_eval(os.environ["NOTIFYTYPE"]) 52 | if "DRAWAMOUNT" in os.environ and os.environ["DRAWAMOUNT"].strip(): 53 | DRAWAMOUNT = ast.literal_eval(os.environ["DRAWAMOUNT"]) 54 | 55 | 56 | def getTemplate(headers, functionId): 57 | """请求模板""" 58 | functionURL = f"https://mqqapi.reader.qq.com/mqq/{functionId}" 59 | delay() 60 | data = requests.get(functionURL, headers=ast.literal_eval(headers)).json() 61 | return data 62 | 63 | 64 | def qqreadtask(headers): 65 | """获取任务列表""" 66 | task_data = getTemplate(headers, "red_packet/user/page?fromGuid=")['data'] 67 | return task_data 68 | 69 | 70 | def qqreadmytask(headers): 71 | """获取“我的”页面任务""" 72 | mytask_data = getTemplate(headers, "v1/task/list")['data']['taskList'] 73 | return mytask_data 74 | 75 | 76 | def qqreadinfo(headers): 77 | """获取用户名""" 78 | info_data = getTemplate(headers, "user/init")['data'] 79 | return info_data 80 | 81 | 82 | def qqreadticket(headers): 83 | """书券签到""" 84 | qqreadticketurl = "https://mqqapi.reader.qq.com/mqq/sign_in/user" 85 | delay() 86 | ticket_data = requests.post( 87 | qqreadticketurl, headers=ast.literal_eval(headers)).json()['data'] 88 | return ticket_data 89 | 90 | 91 | def qqreadsign(headers): 92 | """每日打卡""" 93 | sign_data = getTemplate(headers, "red_packet/user/clock_in/page")['data'] 94 | return sign_data 95 | 96 | def qqreadsign_new(headers): 97 | """每日打卡""" 98 | sign_data = getTemplate(headers, "red_packet/user/clock_in") 99 | return sign_data 100 | 101 | def qqreadsign2(headers): 102 | """每日打卡翻倍""" 103 | sign2_data = getTemplate(headers, "red_packet/user/clock_in_video") 104 | return sign2_data 105 | 106 | 107 | def qqreadtodayread(headers): 108 | """每日阅读""" 109 | todayread_data = getTemplate(headers, "red_packet/user/read_book") 110 | return todayread_data 111 | 112 | 113 | def qqreadvideo(headers): 114 | """视频奖励""" 115 | video_data = getTemplate(headers, "red_packet/user/watch_video") 116 | return video_data 117 | 118 | 119 | def qqreadbox(headers): 120 | """宝箱奖励""" 121 | box_data = getTemplate(headers, "red_packet/user/treasure_box") 122 | return box_data 123 | 124 | 125 | def qqreadbox2(headers): 126 | """宝箱奖励翻倍""" 127 | box2_data = getTemplate(headers, "red_packet/user/treasure_box_video") 128 | return box2_data 129 | 130 | 131 | def qqreadwktime(headers): 132 | """获取本周阅读时长""" 133 | wktime_data = getTemplate(headers, "v1/bookShelfInit")['data']['readTime'] 134 | return wktime_data 135 | 136 | 137 | def qqreadwkpickinfo(headers): 138 | """周阅读时长奖励查询""" 139 | wkpickinfo_data = getTemplate(headers, "pickPackageInit")['data'] 140 | return wkpickinfo_data 141 | 142 | 143 | def qqreadwkpick(headers, num): 144 | """周阅读时长奖励领取""" 145 | wkpick_data = getTemplate(headers, f"pickPackage?readTime={num}") 146 | return wkpick_data 147 | 148 | 149 | def qqreadtodaytime(headers, bidnum): 150 | """获取本日阅读时长""" 151 | bid = re.findall(r'bid=(\d+)&', bidnum)[0] 152 | todaytime_data = getTemplate(headers, f"page/config?router=%2Fpages%2Fbook-read%2Findex&options=%7B%22bid%22%3A%22{bid}%22%7D")[ 153 | 'data']['pageParams']['todayReadSeconds'] 154 | return todaytime_data//60 155 | 156 | 157 | def qqreadtodaygift(headers, sec): 158 | """本日阅读时长奖励""" 159 | todygift_data = getTemplate( 160 | headers, f"red_packet/user/read_time?seconds={sec}")['data'] 161 | return todygift_data 162 | 163 | 164 | def qqreadaddtime(headers, addtimeurl): 165 | """上传阅读时长""" 166 | sectime = random.randint(TIME*60*1000, (TIME+1)*60*1000) 167 | findtime = re.compile(r'readTime=(\d+)&read_') 168 | findtime1 = re.compile(r'readTime%22%3A(\d+)%2C') 169 | url = re.sub(findtime.findall(addtimeurl)[ 170 | 0], str(sectime), str(addtimeurl)) 171 | url = re.sub(findtime1.findall(addtimeurl)[ 172 | 0], str(sectime), str(addtimeurl)) 173 | delay() 174 | addtime_data = requests.get(url, headers=ast.literal_eval(headers)).json() 175 | return addtime_data 176 | 177 | 178 | def qqreadssr(headers, sec): 179 | """每日阅读时长奖励""" 180 | readssr_data = getTemplate( 181 | headers, f"red_packet/user/read_time?seconds={sec}") 182 | return readssr_data 183 | 184 | 185 | def qqreadwithdrawinfo(headers): 186 | """查询提现信息""" 187 | withdrawinfo_data = getTemplate( 188 | headers, f"red_packet/user/withdraw/list?pn=1")['data']['list'][0] 189 | return withdrawinfo_data 190 | 191 | 192 | def qqreadwithdrawal(headers, amount): 193 | """提现""" 194 | qqreadwithdrawalurl = f"https://mqqapi.reader.qq.com/mqq/red_packet/user/withdraw?amount={amount}" 195 | delay() 196 | withdrawal_data = requests.post( 197 | qqreadwithdrawalurl, headers=ast.literal_eval(headers)).json() 198 | if withdrawal_data['data']['code'] == 0: 199 | msg = withdrawal_data['msg'] 200 | else: 201 | msg = withdrawal_data['data']['msg'] 202 | return msg 203 | 204 | 205 | def qqreadtrack(headers, data: str): 206 | """Track""" 207 | qqreadtrackurl = "https://mqqapi.reader.qq.com/log/v4/mqq/track" 208 | finddis = re.compile(r'"dis".*?(\d{13})') 209 | data = re.sub(finddis.findall(data)[ 210 | 0], str(int(time.time()*1000)), str(data)) 211 | delay() 212 | track_data = requests.post( 213 | qqreadtrackurl, data=json.dumps(ast.literal_eval(data)), headers=ast.literal_eval(headers)).json() 214 | return track_data 215 | 216 | 217 | def totalAmount(headers) -> str: 218 | """统计今日获得金币""" 219 | totalamount = 0 220 | for pn in range(12): 221 | url = f'https://mqqapi.reader.qq.com/mqq/red_packet/user/trans/list?pn={pn+1}' 222 | amount_data = requests.get(url, headers=ast.literal_eval(headers)).json()['data']['list'] 223 | for i in amount_data: 224 | if i['createTime'] >= getTimestamp(): 225 | totalamount += i['amount'] 226 | return str(totalamount) 227 | 228 | 229 | def gettime(): 230 | """获取北京时间""" 231 | bj_dt = datetime.utcnow()+timedelta(hours=8) 232 | return bj_dt 233 | 234 | 235 | def getTimestamp() -> int: 236 | """获取当日0点时间戳""" 237 | bj_dt = (datetime.utcnow()+timedelta(hours=8) 238 | ).strftime('%Y-%m-%d') + " 00:00:00" 239 | timeArray = time.strptime(bj_dt, "%Y-%m-%d %H:%M:%S") 240 | timeStamp = int(time.mktime(timeArray)*1000) 241 | return timeStamp 242 | 243 | 244 | def delay(): 245 | """延时""" 246 | time.sleep(DELAYSEC) 247 | 248 | 249 | def sendmsg(title: str, content: str): 250 | """发送通知""" 251 | notification.notify(title, content) 252 | 253 | 254 | def start(index, secrets): 255 | print(f"\n============开始运行第{index+1}个账号===========") 256 | start_time = time.time() 257 | tz = "" 258 | info_data = qqreadinfo(secrets[0]) 259 | todaytime_data = qqreadtodaytime(secrets[0], secrets[2]) 260 | wktime_data = qqreadwktime(secrets[0]) 261 | print(f"Track update {qqreadtrack(secrets[0], secrets[1])['msg']}") 262 | task_data = qqreadtask(secrets[0]) 263 | mytask_data = qqreadmytask(secrets[0]) 264 | 265 | tz += f"=== {gettime().strftime('%Y-%m-%d %H:%M:%S')} ===\n" 266 | tz += f"=== 📣系统通知📣 ===\n" 267 | tz += f"【用户信息】{info_data['user']['nickName']}\n" 268 | tz += f"【账户余额】{task_data['user']['amount']}金币\n" 269 | tz += f"【今日阅读】{todaytime_data}分钟\n" 270 | tz += f"【本周阅读】{wktime_data}分钟\n" 271 | tz += f"【{task_data['taskList'][0]['title']}】{task_data['taskList'][0]['amount']}金币,{task_data['taskList'][0]['actionText']}\n" 272 | tz += f"【{task_data['taskList'][1]['title']}】{task_data['taskList'][1]['amount']}金币,{task_data['taskList'][1]['actionText']}\n" 273 | tz += f"【{task_data['taskList'][2]['title']}】{task_data['taskList'][2]['amount']}金币,{task_data['taskList'][2]['actionText']}\n" 274 | tz += f"【{task_data['taskList'][3]['title']}】{task_data['taskList'][3]['amount']}金币,{task_data['taskList'][3]['actionText']}\n" 275 | tz += f"【第{task_data['invite']['issue']}期】时间{task_data['invite']['dayRange']} [已邀请{task_data['invite']['inviteCount']}人,再邀请{task_data['invite']['nextInviteConfig']['count']}人获得{task_data['invite']['nextInviteConfig']['amount']}金币]\n" 276 | tz += f"【{task_data['fans']['title']}】{task_data['fans']['fansCount']}个好友,{task_data['fans']['todayAmount']}金币\n" 277 | tz += f"【宝箱任务{task_data['treasureBox']['count'] + 1}】{task_data['treasureBox']['tipText']}\n" 278 | 279 | if task_data['treasureBox']['doneFlag'] == 0: 280 | box_data = qqreadbox(secrets[0]) 281 | if box_data['code'] == 0: 282 | tz += f"【宝箱奖励{box_data['data']['count']}】获得{box_data['data']['amount']}金币\n" 283 | 284 | for i in range(len(task_data['taskList'])): 285 | if task_data['taskList'][i]['title'].find("立即阅读") != -1 and task_data['taskList'][i]['doneFlag'] == 0: 286 | todayread_data = qqreadtodayread(secrets[0]) 287 | if todayread_data['code'] == 0: 288 | tz += f"【每日阅读】获得{todayread_data['data']['amount']}金币\n" 289 | 290 | if task_data['taskList'][i]['title'].find("打卡") != -1: 291 | sign_data = qqreadsign(secrets[0]) 292 | if task_data['taskList'][i]['doneFlag'] == 0: 293 | qqreadsign_new(secrets[0]) 294 | tz += f"【今日打卡】获得{sign_data['todayAmount']}金币,已连续签到{sign_data['clockInDays']}天\n" 295 | if sign_data['videoDoneFlag'] == 0: 296 | sign2_data = qqreadsign2(secrets[0]) 297 | if sign2_data['code'] == 0: 298 | tz += f"【打卡翻倍】获得{sign2_data['data']['amount']}金币\n" 299 | 300 | if task_data['taskList'][i]['title'].find("视频") != -1 and task_data['taskList'][i]['doneFlag'] == 0: 301 | video_data = qqreadvideo(secrets[0]) 302 | if video_data['code'] == 0: 303 | tz += f"【视频奖励】获得{video_data['data']['amount']}金币\n" 304 | 305 | if task_data['taskList'][i]['title'].find("阅读任务") != -1 and task_data['taskList'][i]['doneFlag'] == 0: 306 | if todaytime_data >= 1 and todaytime_data < 15: 307 | todaygift_data = qqreadtodaygift(secrets[0], 30) 308 | if todaygift_data['amount'] > 0: 309 | tz += f"【阅读金币1】获得{todaygift_data['amount']}金币\n" 310 | if todaytime_data >= 5 and todaytime_data < 30: 311 | time.sleep(2) 312 | todaygift_data = qqreadtodaygift(secrets[0], 300) 313 | if todaygift_data['amount'] > 0: 314 | tz += f"【阅读金币2】获得{todaygift_data['amount']}金币\n" 315 | if todaytime_data >= 30: 316 | time.sleep(2) 317 | todaygift_data = qqreadtodaygift(secrets[0], 1800) 318 | if todaygift_data['amount'] > 0: 319 | tz += f"【阅读金币3】获得{todaygift_data['amount']}金币\n" 320 | 321 | for i in range(len(mytask_data)): 322 | if mytask_data[i]['title'].find("每日签到") != -1 and mytask_data[i]['doneFlag'] == 0: 323 | ticket_data = qqreadticket(secrets[0]) 324 | if ticket_data['takeTicket'] > 0: 325 | tz += f"【书券签到】获得{ticket_data['takeTicket']}书券\n" 326 | 327 | if wktime_data >= 1200: 328 | wkpickinfo_data = qqreadwkpickinfo(secrets[0]) 329 | package = ['10', '10', '20', '30', '50', '80', '100', '120'] 330 | if wkpickinfo_data[-1]['isPick'] == False: 331 | for m, i in enumerate(wkpickinfo_data): 332 | info = getTemplate( 333 | secrets[0], f"pickPackage?readTime={i['readTime']}") 334 | if info['code'] == 0: 335 | tz += f"【周时长奖励{m+1}】领取{package[m]}书券\n" 336 | else: 337 | tz += "【周时长奖励】已全部领取\n" 338 | 339 | if task_data['treasureBox']['videoDoneFlag'] == 0: 340 | time.sleep(6) 341 | box2_data = qqreadbox2(secrets[0]) 342 | if box2_data['code'] == 0: 343 | tz += f"【宝箱翻倍】获得{box2_data['data']['amount']}金币\n" 344 | 345 | if todaytime_data//60 <= LIMIT_TIME: 346 | addtime_data = qqreadaddtime(secrets[0], secrets[2]) 347 | if addtime_data['code'] == 0: 348 | tz += f"【阅读时长】成功上传{TIME}分钟\n" 349 | 350 | if DRAWAMOUNT != 0 and task_data['user']['amount'] >= DRAWAMOUNT*10000 and gettime().hour == 21: 351 | withdrawinfo_data = qqreadwithdrawinfo(secrets[0])['createTime'] 352 | if withdrawinfo_data < getTimestamp(): 353 | withdrawal_data = qqreadwithdrawal(secrets[0], DRAWAMOUNT*10000) 354 | sendmsg("企鹅读书提现通知", f"[{info_data['user']['nickName']}] 提现{DRAWAMOUNT}元:{withdrawal_data}") 355 | tz += f"【自动提现】提现{DRAWAMOUNT}元({withdrawal_data})\n" 356 | 357 | tz += f"【今日收益】{totalAmount(secrets[0])}金币\n" 358 | 359 | tz += f"\n🕛耗时:{time.time()-start_time}秒" 360 | print(tz) 361 | 362 | if NOTIFYTYPE == 1: 363 | sendmsg("企鹅读书通知", tz) 364 | if NOTIFYTYPE == 2 and task_data['treasureBox']['doneFlag'] == 0: 365 | sendmsg("企鹅读书通知", tz) 366 | if NOTIFYTYPE == 3 and task_data['treasureBox']['doneFlag'] == 0 and task_data['treasureBox']['count'] % 15 == 0: 367 | sendmsg("企鹅读书通知", tz) 368 | 369 | 370 | if __name__ == "__main__": 371 | for index, secrets in enumerate(qqreadCookie.get_cookies()): 372 | start(index, secrets) 373 | -------------------------------------------------------------------------------- /qqreadCookie.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import time 4 | import os 5 | import re 6 | import ast 7 | import notification 8 | 9 | 10 | """ 11 | 需要推送通知的,修改notification.py 12 | """ 13 | 14 | ############################### 15 | # 方案1 本地执行、云服务器、云函数等等 下载到本地,填写参数,执行 16 | 17 | # qqreadheaders参数填写,填写完注意不要上传 18 | # 如果有其它账号,还需要将qqreadheaders2填写进下面的qqreadheadersLists 19 | qqreadheaders1 = '{}' 20 | qqreadheaders2 = '{}' 21 | 22 | # qqreadbody参数填写,填写完注意不要上传 23 | # 如果有其它账号,还需要将qqreadtimeheaders2填写进下面的qqreadheadersLists 24 | qqreadbodys1 = '{}' 25 | qqreadbodys2 = '{}' 26 | 27 | # qqreadheaders参数填写,填写完注意不要上传 28 | # 如果有其它账号,还需要将qqreadheaders2填写进下面的qqreadheadersLists 29 | qqreadtimeurl1 = "" 30 | qqreadtimeurl2 = "" 31 | 32 | # 如为多账号,请修改下面参数 33 | qqreadheadersLists = [qqreadheaders1, ] 34 | qqreadbodysLists = [qqreadbodys1, ] 35 | qqreadtimeurlLists = [qqreadtimeurl1, ] 36 | 37 | qqreadLists = list( 38 | zip(qqreadheadersLists, qqreadbodysLists, qqreadtimeurlLists)) 39 | 40 | #################################### 41 | # 方案2 GitHub action 自动运行 各参数读取自secrets 42 | 43 | 44 | if "QQREADHEADERS" and "QQREADBODYS" and "QQREADTIMEURL" in os.environ: 45 | qqreadheaders = os.environ["QQREADHEADERS"].split('\n') 46 | qqreadbodys = os.environ["QQREADBODYS"].split('\n') 47 | qqreadtimeurl = os.environ["QQREADTIMEURL"].split('\n') 48 | qqreadLists = [] 49 | if len(qqreadheaders) == len(qqreadbodys) and len(qqreadbodys) == len(qqreadtimeurl): 50 | qqreadLists = list( 51 | zip(qqreadheaders, qqreadbodys, qqreadtimeurl)) 52 | else: 53 | print("各项Secrets数量不符,请修改!") 54 | 55 | 56 | 57 | ####################################### 58 | 59 | 60 | def valid(qqheaders): 61 | headers = ast.literal_eval(qqheaders[0]) 62 | response = requests.get( 63 | 'https://mqqapi.reader.qq.com/mqq/user/init', headers=headers) 64 | if response.json()["data"]['isLogin'] == False: 65 | QQNUM = re.findall(r'ywguid=(.*?);ywkey', headers['Cookie'])[0] 66 | print(f"""## {QQNUM}: headers过期""") 67 | notification.notify( 68 | f"""## QQ账号【{QQNUM}】 headers过期""", f"""## 账号【{QQNUM}】 headers过期 ,及时修改""") 69 | return False 70 | return True 71 | 72 | 73 | def get_cookies(): 74 | return [i for i in qqreadLists if valid(i)] 75 | 76 | 77 | if __name__ == "__main__": 78 | print(">>>检查有效性") 79 | for i in get_cookies(): 80 | print(i) 81 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | --------------------------------------------------------------------------------