├── requirements.txt
├── .gitignore
├── .github
└── workflows
│ └── main.yml
├── README.md
├── settings.py
└── main.py
/requirements.txt:
--------------------------------------------------------------------------------
1 | certifi==2023.7.22
2 | chardet==4.0.0
3 | idna==2.10
4 | requests==2.31.0
5 | urllib3==1.26.18
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 |
3 | # C extensions
4 | *.so
5 |
6 | # Packages
7 | *.egg
8 | *.egg-info
9 | dist
10 | build
11 | eggs
12 | parts
13 | bin
14 | var
15 | sdist
16 | develop-eggs
17 | .installed.cfg
18 | lib
19 | lib64
20 | __pycache__
21 |
22 | # Installer logs
23 | pip-log.txt
24 |
25 | # Unit tmp / coverage reports
26 | .coverage
27 | .tox
28 | nosetests.xml
29 | .pytest_cache
30 |
31 | # Translations
32 | *.mo
33 |
34 | # Mr Developer
35 | .mr.developer.cfg
36 | .project
37 | .pydevproject
38 |
39 | # temp file
40 | .DS_Store
41 | *.pkl
42 |
43 | # venv
44 | .venv/
45 |
46 | # Cookiecutter
47 | output/
48 |
49 | # vscode
50 | .vscode
51 |
52 | # notebooks
53 | notebooks/
54 |
55 | # idea
56 | .idea
57 | .python-version
58 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: "Tencent Video Helper"
2 |
3 | on:
4 | schedule:
5 | - cron: "0 1 * * *" # scheduled at 09:00 (UTC+8) everyday
6 | workflow_dispatch:
7 |
8 | env:
9 | RUN_ENV: 'prod'
10 | TZ: 'Asia/Shanghai'
11 | SCKEY: ${{ secrets.SCKEY }}
12 | AUTH_REFRESH_URL: ${{ secrets.AUTH_REFRESH_URL }}
13 | AUTH_REFRESH_COOKIE: ${{ secrets.AUTH_REFRESH_COOKIE }}
14 |
15 | jobs:
16 | build:
17 | runs-on: ubuntu-latest
18 | # if: github.ref == 'refs/heads/master'
19 |
20 | steps:
21 | - name: Checkout master
22 | uses: actions/checkout@v2
23 | with:
24 | fetch-depth: 0
25 | ref: master
26 |
27 | - name: Set up python
28 | uses: actions/setup-python@v2
29 | with:
30 | python-version: 3.8
31 |
32 | - name: Random sleep
33 | if: github.event_name == 'schedule'
34 | run: sleep $(shuf -i 10-300 -n 1)
35 |
36 | - name: Run sign
37 | run: |
38 | python -m pip install --upgrade pip
39 | pip install -r requirements.txt
40 | python3 ./main.py
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tencent Video Helper
4 |
5 |
6 | [](https://github.com/PomeloWang/my-actions/issues)
7 | [](https://github.com/PomeloWang/my-actions/graphs/contributors)
8 | 
9 |
10 |
11 | > 吹水交流:[130516740](https://qm.qq.com/cgi-bin/qm/qr?k=_M9lYFxkYD7yQQR2btyG3pkZWFys_I-l&authKey=evGDzE2eFVBm46jsHpgcWrokveg70Z9GKl3H45o0oJuia620UGeO27lDPG9gKb/2&noverify=0)
12 | ### 部署
13 | - `AUTH_REFRESH_URL` 腾讯视频官网, 打开浏览器控制台网络搜索`auth_refresh`相关的请求[图片教程](https://cdn.jsdelivr.net/gh/BlueskyClouds/Script/img/2020/11/1/img/v_1.jpg)
14 | - `AUTH_REFRESH_COOKIE` 从上面找到的请求中复制`Cookie`[图片教程](https://cdn.jsdelivr.net/gh/BlueskyClouds/Script/img/2020/11/1/img/v_2.jpg)
15 | - `SCKEY` 详情参考 [SERVER酱官网](http://sc.ftqq.com/3.version)
16 |
17 |
18 | #### 其他
19 | - 如果二次签到失败,尝试手动从手机`app`签到一次。私信任意用户然后发送并点击链接`http://v.qq.com/x/bu/mobile_checkin`, 点击链接后会签到一次。然后次日在观察执行结果
20 |
--------------------------------------------------------------------------------
/settings.py:
--------------------------------------------------------------------------------
1 | # settings
2 | import logging
3 |
4 | import os
5 |
6 | __all__ = ['log', 'CONFIG']
7 |
8 | logging.basicConfig(
9 | level=logging.INFO,
10 | format='%(asctime)s %(levelname)s %(message)s',
11 | datefmt='%Y-%m-%dT%H:%M:%S')
12 |
13 |
14 | log = logger = logging
15 |
16 |
17 | default_user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' \
18 | 'Chrome/64.0.3282.204 Safari/537.36 '
19 |
20 |
21 | class _Config:
22 | AUTH_REFRESH_URL = os.environ['AUTH_REFRESH_URL']
23 | AUTH_REFRESH_COOKIE: dict = os.environ['AUTH_REFRESH_COOKIE']
24 | HEADERS = {
25 | 'Referer': 'https://v.qq.com',
26 | 'User-Agent': default_user_agent,
27 | 'Cookie': AUTH_REFRESH_COOKIE
28 | }
29 | SIGN_URL = 'https://vip.video.qq.com/fcgi-bin/comm_cgi?name=hierarchical_task_system&cmd=2'
30 | MOBILE_CHECKIN = 'http://v.qq.com/x/bu/mobile_checkin?isDarkMode=0&uiType=REGULAR'
31 | SCKEY = os.environ.get('SCKEY', '')
32 |
33 |
34 | class ProductionConfig(_Config):
35 | LOG_LEVEL = logging.INFO
36 |
37 |
38 | class DevelopmentConfig(_Config):
39 | LOG_LEVEL = logging.DEBUG
40 |
41 |
42 | RUN_ENV = os.environ.get('RUN_ENV', 'dev')
43 | if RUN_ENV == 'dev':
44 | CONFIG = DevelopmentConfig()
45 | else:
46 | CONFIG = ProductionConfig()
47 |
48 | log.basicConfig(level=CONFIG.LOG_LEVEL)
49 |
50 |
51 | MESSGAE_TEMPLATE = '''
52 | ```
53 | {today:#^30}
54 | 🔅[{nick}]
55 | 签到积分: {checkin_score}
56 | 签到结果: {message}
57 | 手机签到: {mobile_checkin}
58 | {end:#^30}
59 | ```'''
60 |
61 | CONFIG.MESSGAE_TEMPLATE = MESSGAE_TEMPLATE
62 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import json
3 | import re
4 | import urllib.parse
5 |
6 | import requests
7 |
8 | from settings import *
9 |
10 | request = requests.session()
11 |
12 | today = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
13 |
14 |
15 | def to_python(json_str: str):
16 | return json.loads(json_str)
17 |
18 |
19 | def to_json(obj):
20 | return json.dumps(obj, indent=4, ensure_ascii=False)
21 |
22 |
23 | def cookie_2_python(cookie):
24 | obj = {}
25 | for header in cookie.split(";"):
26 | param = header.split("=")
27 | obj[param[0].strip()] = param[1]
28 | return obj
29 |
30 |
31 | def cookie_2_param(cookie_obj: dict):
32 | param = ""
33 | for k, v in cookie_obj.items():
34 | param += "{k}={v}; ".format(**locals())
35 | return param[:-2]
36 |
37 |
38 | def decode_json_str(s):
39 | pattern = re.compile('{.*}')
40 | return json.loads(pattern.search(s).group())
41 |
42 |
43 | def decode_urldecode(s):
44 | return urllib.parse.unquote(s)
45 |
46 |
47 | def notify(title, message):
48 | if not CONFIG.SCKEY:
49 | log.info("未配置SCKEY,正在跳过推送")
50 | return
51 |
52 | log.info("准备推送通知...")
53 | urlencode = urllib.parse.urlencode
54 | url = 'https://sctapi.ftqq.com/{}.send?{}&{}'.format(CONFIG.SCKEY, urlencode({'title': title}), urlencode({'desp': message}))
55 |
56 | try:
57 | response = to_python(requests.post(url).text)
58 | # {"code":0,"message":"","data":{"pushid":"1111","readkey":"xxxx","error":"SUCCESS","errno":0}}
59 | log.info('推送结果: {}'.format(response.get('data', {'error': 'no data'}).get('error', '')))
60 | except Exception as e:
61 | log.error('{}: {}'.format("推送异常", e))
62 | return log.info('任务结束')
63 |
64 |
65 |
66 |
67 | def main():
68 | message = {
69 | 'today': today,
70 | 'ret': -1,
71 | 'checkin_score': "-1",
72 | 'mobile_checkin': "失败",
73 | 'end': ''
74 | }
75 | # 主要是判断是否登陆成功以及刷新cookie参数
76 | response = request.get(url=CONFIG.AUTH_REFRESH_URL, headers=CONFIG.HEADERS).text
77 | auth_refresh_obj = decode_json_str(response)
78 |
79 | if (auth_refresh_obj.get('errcode', 9999) != 0) or (not auth_refresh_obj.get('nick', None)):
80 | log.error("刷新cookie参数失败, {msg}".format(**auth_refresh_obj))
81 | message.update({
82 | 'ret': auth_refresh_obj.get('errcode', -1),
83 | 'nick': decode_urldecode(auth_refresh_obj.get('nick', "刷新Cookie参数失败, 未获取到用户信息")),
84 | })
85 | log.error("签到失败", CONFIG.MESSGAE_TEMPLATE.format(**message))
86 | notify("腾讯视频 签到失败", CONFIG.MESSGAE_TEMPLATE.format(**message))
87 | exit(-1)
88 |
89 | old_cookie_obj = cookie_2_python(CONFIG.HEADERS['Cookie'])
90 | need_update_fields = {
91 | 'vuserid': 'vqq_vuserid',
92 | 'vusession': 'vqq_vusession',
93 | 'access_token': 'vqq_access_token'
94 | }
95 |
96 | log.info("更新Cookie参数")
97 | # 更新Cookie参数
98 | for k, v in need_update_fields.items():
99 | old_cookie_obj[v] = auth_refresh_obj[k]
100 |
101 | # 使用更新过的Cookie参数替换CONFIG.HEADERS中的Cookie参数
102 | CONFIG.HEADERS.update({
103 | 'Cookie': cookie_2_param(old_cookie_obj),
104 | 'Referer': 'https://m.v.qq.com'
105 | })
106 | log.info("更新Cookie参数成功, 开始签到")
107 |
108 | # QZOutputJson=({ "ret": 0,"checkin_score": 0,"msg":"OK"});
109 | sign_response = request.get(url=CONFIG.SIGN_URL, headers=CONFIG.HEADERS).text
110 | sign_obj = decode_json_str(sign_response)
111 |
112 | message.update({
113 | 'ret': sign_obj['ret'],
114 | 'nick': decode_urldecode(auth_refresh_obj['nick']),
115 | 'message': sign_obj['msg'],
116 | 'checkin_score': sign_obj.get('checkin_score', 0) or "👀 今日已签到了哦"
117 |
118 | })
119 | # TODO 手机签到失败不会重置任务状态
120 | m_checkin_response = request.get(url=CONFIG.MOBILE_CHECKIN, headers=CONFIG.HEADERS).text
121 |
122 | if "page_signin_detail" in m_checkin_response:
123 | message.update({'mobile_checkin': "成功"})
124 | log.info("签到成功 {}".format(CONFIG.MESSGAE_TEMPLATE.format(**message)))
125 | notify("腾讯视频 签到成功", CONFIG.MESSGAE_TEMPLATE.format(**message))
126 |
127 |
128 | if __name__ == '__main__':
129 | try:
130 | main()
131 | except Exception as e:
132 | notify("腾讯视频 签到失败", {
133 | "msg": "请前往执行日志查看详情",
134 | "err": str(e)
135 | })
136 | raise e
137 |
--------------------------------------------------------------------------------