├── .gitignore ├── data.json ├── main.py ├── main2.py └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /data.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022.1 by WuliAPO 2 | # Copyright © cjlu.online . All rights reserved. 3 | 4 | # modified by @Dimlitter 2022/3/1 5 | # personal profile: https://github.com/Dimlitter 6 | import json 7 | import random 8 | import time 9 | import requests 10 | import datetime 11 | 12 | class QCZJ_Youth_Learning: 13 | ''' 14 | · self.session : 统一的session管理 15 | 16 | · 支持每日签到与阅读文章 17 | 18 | · 每周观看网课 19 | ''' 20 | def __init__(self,nid,cardNo,openid,nickname): 21 | self.nid = nid 22 | self.cardNo = cardNo 23 | self.openid = openid 24 | self.nickname = nickname 25 | 26 | self.sleep_time = random.randint(5,10) 27 | self.session = requests.session() 28 | 29 | self.headers = { 30 | 'Host': 'qczj.h5yunban.com', 31 | 'Connection': 'keep-alive', 32 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K11AC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.99 XWEB/3185 MMWEBSDK/20220105 Mobile Safari/537.36 MMWEBID/4365 MicroMessenger/8.0.19.2080(0x2800133D) Process/toolsmp WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64', 33 | 'Accept': 'application/json, text/javascript, */*; q=0.01', 34 | 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7', 35 | 'Accept-Encoding': 'gzip, deflate, br', 36 | 37 | 'X-Requested-With': 'XMLHttpRequest', 38 | 'Content-Type': 'application/json;charset=UTF-8', 39 | 40 | 'Sec-Fetch-Site': 'same-origin', 41 | 'Sec-Fetch-Mode': 'cors', 42 | 'Sec-Fetch-Dest': 'empty', 43 | } 44 | 45 | #获取AccessToken 46 | def getAccessToken(self,openid,nickname): 47 | headers = { 48 | 'Upgrade-Insecure-Requests': '1', 49 | 'Sec-Fetch-Site': 'none', 50 | 'Sec-Fetch-Mode': 'navigate', 51 | 'Sec-Fetch-User': '?1', 52 | 'Sec-Fetch-Dest': 'document', 53 | } 54 | tempheaders = self.headers.copy() 55 | tempheaders.update(headers) 56 | time_stamp = str(int(time.time()))#获取时间戳 57 | url = "https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/login/we-chat/callback?callback=https%3A%2F%2Fqczj.h5yunban.com%2Fqczj-youth-learning%2Findex.php&scope=snsapi_userinfo&appid=wx56b888a1409a2920&openid="+openid+"&nickname="+ nickname +"&headimg=&time="+time_stamp+"&source=common&sign=&t="+time_stamp 58 | res = self.session.get(url,headers=tempheaders) 59 | access_token = res.text[45:81]#比较懒,直接截取字符串了 60 | print("获取到AccessToken:",access_token) 61 | return access_token 62 | 63 | #获取当前最新的课程代号 64 | def getCurrentCourse(self,access_token): 65 | headers = { 66 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/signUp.php?rom=1', 67 | } 68 | headers.update(self.headers) 69 | url = "https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/common-api/course/current?accessToken="+access_token 70 | res = self.session.get(url) 71 | res_json = json.loads(res.text) 72 | if(res_json["status"]==200):#验证正常 73 | print("获取到最新课程代号:", res_json["result"]["id"]) 74 | return res_json["result"]["id"] 75 | else: 76 | print("获取最新课程失败!退出程序") 77 | print(res.text) 78 | exit(0) 79 | 80 | def getLatestCourseRecord(self,access_token): 81 | headers = { 82 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/mine.php', 83 | } 84 | headers.update(self.headers) 85 | url = f"https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/user-api/course/records/v2?accessToken="+access_token+"&pageSize=5&pageNum=1&desc=createTime" 86 | res = self.session.get(url, headers=headers) 87 | res_json = json.loads(res.text) 88 | idx = 0 89 | while len(res_json["result"]["list"][idx]["list"]) == 0: 90 | idx += 1 91 | if res_json["status"] == 200: 92 | print("获取到最新课程记录:", res_json["result"]["list"][idx]["id"]) 93 | return res_json["result"]["list"][idx]["id"] 94 | else: 95 | print("获取最新课程记录失败!退出程序") 96 | print(res.text) 97 | exit(0) 98 | 99 | #签到 并获取签到记录 100 | def getJoin(self,access_token,current_course,nid,cardNo): 101 | headers = { 102 | 'Content-Length': '80', 103 | 'Origin': 'https://qczj.h5yunban.com', 104 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/signUp.php?rom=1', 105 | } 106 | headers.update(self.headers) 107 | data = { 108 | "course": current_course,# 大学习期次的代码,如C0046,本脚本已经帮你获取啦 109 | "subOrg": None, 110 | "nid": nid, # 团组织编号,形如N003************ 111 | "cardNo": cardNo # 打卡昵称 112 | } 113 | url = "https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/user-api/course/join?accessToken="+access_token 114 | res = self.session.post(url,json=data,headers=headers) #特别注意,此处应选择json格式发送data数据 115 | print("签到结果:",res.text) 116 | res_json = json.loads(res.text) 117 | if(res_json["status"]==200):#验证正常 118 | print("似乎签到成功了") 119 | return True 120 | else: 121 | print("签到失败!") 122 | exit(0) 123 | 124 | def check(self,access_token): 125 | headers1 = { 126 | 'Content-Length': '2', 127 | 'Origin': 'https://qczj.h5yunban.com', 128 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/mine.php', 129 | } 130 | url = 'https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/user-api/sign-in?accessToken='+access_token 131 | data = { 132 | #'accessToken': access_token, 133 | } 134 | try: 135 | res = self.session.post(url,json = data,headers=headers1) 136 | except : 137 | print("网络错误,请检查网络") 138 | print("尝试重新签到,等待15s") 139 | time.sleep(15) 140 | try: 141 | res = self.session.post(url,json = data,headers=headers1) 142 | except : 143 | print("签到失败!") 144 | return False 145 | res = json.loads(res.text) 146 | if res["status"]==200: 147 | print("访问成功") 148 | if res['result'] == False: 149 | print("今天已经签到过了") 150 | else: 151 | print("今天签到成功!") 152 | else: 153 | print("访问失败") 154 | 155 | url2 = 'https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/user-api/sign-in/records?accessToken=' + access_token + '&date=' + datetime.datetime.now().strftime('%Y-%m') 156 | headers2 = { 157 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/mine.php', 158 | } 159 | headers2.update(self.headers) 160 | try: 161 | res2 = self.session.get(url2,headers=headers2) 162 | if res2.status_code == 200: 163 | print("签到记录存在!") 164 | print(res2.text) 165 | else: 166 | print("签到记录失败") 167 | except: 168 | print("网络错误,不影响签到") 169 | 170 | def read(self,access_token): 171 | headers = { 172 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/learn.php', 173 | 'Content-Length': '2', 174 | 'Origin': 'https://qczj.h5yunban.com', 175 | } 176 | headers.update(self.headers) 177 | numbers = [random.randint(470017, 470026) for i in range(4)] 178 | for number in numbers: 179 | number = str(number) 180 | url = 'https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/user-api/course/study?accessToken='+access_token+'&id='+ 'C00'+ number 181 | time.sleep(8) 182 | #传的data居然是空的 183 | data = { 184 | #'accessToken': access_token, 185 | #'id': 'C00'+ number, 186 | } 187 | try: 188 | res = self.session.post(url,json = data,headers=headers).json() 189 | if res['status'] == 200: 190 | print("文章"+'C00'+ number +"学习成功") 191 | print(res) 192 | else: 193 | print("文章"+'C00'+ number +"学习失败") 194 | print(res) 195 | except: 196 | print("网络错误,尝试重新学习,等待15s") 197 | time.sleep(15) 198 | try: 199 | res = self.session.post(url,json = data).json() 200 | if res['status'] == 200: 201 | print("文章"+'C00'+ number +"重新学习成功") 202 | except: 203 | print("重新学习失败,退出") 204 | break 205 | 206 | def main(self): 207 | 208 | access_token = self.getAccessToken(self.openid,self.nickname) 209 | 210 | time.sleep(self.sleep_time) 211 | 212 | #添加随机执行 213 | sequence = [1,2,3] 214 | random.shuffle(sequence) 215 | order = {1:'看视频',2:'签到',3:'阅读文章'} 216 | result = '' 217 | for i in sequence: 218 | result = result + order[i] + '------>' 219 | print("本次学习的顺序为:",result,"完成") 220 | 221 | for i in sequence: 222 | if i == 1: #看视频 223 | time.sleep(self.sleep_time) 224 | print("今天是星期",datetime.datetime.now().weekday()+1) 225 | if datetime.datetime.now().weekday() == 0: 226 | current_course = self.getCurrentCourse(access_token) 227 | latest_course = self.getLatestCourseRecord(access_token) 228 | if current_course == latest_course: 229 | print("这个视频已经看过了,不看了") 230 | else: 231 | time.sleep(self.sleep_time) 232 | self.getJoin(access_token,current_course,self.nid,self.cardNo) 233 | else: 234 | print("今天不是周一,不看视频") 235 | if i == 2: #签到 236 | time.sleep(self.sleep_time) 237 | self.check(access_token) 238 | if i == 3: #阅读 239 | time.sleep(self.sleep_time) 240 | self.read(access_token) 241 | 242 | if __name__ == '__main__': 243 | 244 | nid = "" # 在这里输入你的团组织编号,形如N003************,请使用抓包软件获取 245 | cardNo = ""# 在这里输入你打卡时用的昵称,可能是学号,可能是姓名 246 | openid = "" #靠自己抓包 247 | nickname = "" #一般是微信账户名 248 | 249 | qndxx = QCZJ_Youth_Learning(nid,cardNo,openid,nickname) 250 | qndxx.main() 251 | 252 | -------------------------------------------------------------------------------- /main2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | from functools import wraps 4 | from types import MethodType 5 | import random 6 | import time 7 | import requests 8 | import json 9 | 10 | 11 | class TimeoutRetry: 12 | max_retry = 3 13 | 14 | def __init__(self, func): 15 | wraps(func)(self) 16 | 17 | def __call__(self, *args, **kwargs): 18 | retry_count = 0 19 | while retry_count < self.max_retry: 20 | try: 21 | return self.__wrapped__(*args, **kwargs) 22 | except requests.RequestException: 23 | print("请求超时,正在重试") 24 | retry_count += 1 25 | raise TimeoutError("重试三次,连接无效,请检查网络") 26 | 27 | def __get__(self, instance, cls): 28 | if instance is None: 29 | return self 30 | else: 31 | return MethodType(self, instance) 32 | 33 | 34 | class YouthLearning: 35 | def __init__(self, open_id, nick_name, name, nid): 36 | self.open_id = open_id 37 | self.nick_name = nick_name 38 | self.name = name 39 | self.nid = nid 40 | 41 | self.headers = { 42 | 'Host': 'qczj.h5yunban.com', 43 | 'Connection': 'keep-alive', 44 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K11AC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (' 45 | 'KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.99 XWEB/3185 MMWEBSDK/20220105 Mobile ' 46 | 'Safari/537.36 MMWEBID/4365 MicroMessenger/8.0.19.2080(0x2800133D) Process/toolsmp ' 47 | 'WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64', 48 | 'Accept': 'application/json, text/javascript, */*; q=0.01', 49 | 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7', 50 | 'Accept-Encoding': 'gzip, deflate, br', 51 | 52 | 'X-Requested-With': 'XMLHttpRequest', 53 | 'Content-Type': 'application/json;charset=UTF-8', 54 | 55 | 'Sec-Fetch-Site': 'same-origin', 56 | 'Sec-Fetch-Mode': 'cors', 57 | 'Sec-Fetch-Dest': 'empty', 58 | } 59 | self.session = requests.session() 60 | 61 | self.access_token = self._get_access_token() 62 | 63 | @staticmethod 64 | def time_sleep(): 65 | time.sleep(random.randint(5, 10)) 66 | 67 | @TimeoutRetry 68 | def _get_access_token(self): 69 | headers = { 70 | 'Upgrade-Insecure-Requests': '1', 71 | 'Sec-Fetch-Site': 'none', 72 | 'Sec-Fetch-Mode': 'navigate', 73 | 'Sec-Fetch-User': '?1', 74 | 'Sec-Fetch-Dest': 'document', 75 | } 76 | temp_headers = self.headers.copy() 77 | temp_headers.update(headers) 78 | time_stamp = str(int(time.time())) 79 | url = f"https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/login/we-chat/callback?callback=https%3A%2F" \ 80 | f"%2Fqczj.h5yunban.com%2Fqczj-youth-learning%2Findex.php&scope=snsapi_userinfo&appid=wx56b888a1409a2920" \ 81 | f"&openid={self.open_id}&nickname={self.nick_name}&headimg=&time={time_stamp}&source=common&sign=&t=" \ 82 | f"{time_stamp} " 83 | res = self.session.get(url, headers=temp_headers, timeout=3) 84 | return res.text[45:81] 85 | 86 | @TimeoutRetry 87 | def _get_current_course(self): 88 | headers = { 89 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/signUp.php?rom=1', 90 | } 91 | headers.update(self.headers) 92 | url = f"https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/common-api/course/current?" \ 93 | f"accessToken={self.access_token}" 94 | res_json = self.session.get(url, headers=headers, timeout=3).json() 95 | if res_json["status"] == 200: 96 | return res_json["result"]["id"] 97 | else: 98 | return "" 99 | 100 | @TimeoutRetry 101 | def _get_latest_course_record(self): 102 | headers = { 103 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/mine.php', 104 | } 105 | headers.update(self.headers) 106 | url = f"https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/user-api/course/records/v2?" \ 107 | f"accessToken={self.access_token}" \ 108 | f"&pageSize=5&pageNum=1&desc=createTime" 109 | res_json = self.session.get(url, headers=headers, timeout=3).json() 110 | idx = 0 111 | while len(res_json["result"]["list"][idx]["list"]) == 0: 112 | idx += 1 113 | if res_json["status"] == 200: 114 | return res_json["result"]["list"][idx]["id"] 115 | else: 116 | return "" 117 | 118 | @TimeoutRetry 119 | def join_course(self, course_id, nid, name): 120 | headers = { 121 | 'Content-Length': '80', 122 | 'Origin': 'https://qczj.h5yunban.com', 123 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/signUp.php?rom=1', 124 | } 125 | headers.update(self.headers) 126 | data = { 127 | "course": course_id, # 大学习期次 128 | "subOrg": None, # 就是空的 129 | "nid": nid, # 团组织编号 130 | "cardNo": name # 打卡昵称 131 | } 132 | url = f"https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/user-api/course/join?" \ 133 | f"accessToken={self.access_token}" 134 | res_json = self.session.post(url, json=data, headers=headers, timeout=3).json() # json发送保证subOrg被转化为null 135 | return res_json["status"] == 200 136 | 137 | @TimeoutRetry 138 | def read_passage(self, passage_id): 139 | headers = { 140 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/learn.php', 141 | 'Content-Length': '2', 142 | 'Origin': 'https://qczj.h5yunban.com', 143 | } 144 | headers.update(self.headers) 145 | url = f'https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/user-api/course/study?' \ 146 | f'accessToken={self.access_token}&id={passage_id}' 147 | res = self.session.post(url, json={}, headers=headers, timeout=3).json() 148 | return res['status'] == 200 149 | 150 | @TimeoutRetry 151 | def sign_in(self): 152 | headers = { 153 | 'Content-Length': '2', 154 | 'Origin': 'https://qczj.h5yunban.com', 155 | 'Referer': 'https://qczj.h5yunban.com/qczj-youth-learning/mine.php', 156 | } 157 | url = f'https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/user-api/sign-in?accessToken={self.access_token}' 158 | res = self.session.post(url, json={}, headers=headers, timeout=3).json() 159 | return res["status"] == 200 and res['result'] 160 | 161 | def main(self, learn_course=False): 162 | passages = [f'C004700{random.randint(17, 26)}' for _ in range(4)] 163 | 164 | # 签到 165 | if self.sign_in(): 166 | print(f"{self.name}签到成功") 167 | else: 168 | print(f"{self.name}已签到或签到失败") 169 | 170 | self.time_sleep() 171 | 172 | # 读文章 173 | for passage_id in passages: 174 | if self.read_passage(passage_id): 175 | print(f"{self.name}已学习{passage_id}") 176 | else: 177 | print(f"{self.name}学习失败{passage_id}") 178 | time.sleep(random.randint(8, 10)) 179 | 180 | # 学视频 181 | if learn_course: 182 | new_course_id = self._get_current_course() 183 | latest_course_id = self._get_latest_course_record() 184 | if new_course_id == latest_course_id: 185 | print(f"{self.name}此前已完成视频课程") 186 | else: 187 | self.time_sleep() 188 | if self.join_course(new_course_id, self.nid, self.name): 189 | print(f"{self.name}已完成视频课程") 190 | else: 191 | print(f"{self.name}视频课程失败") 192 | 193 | 194 | if __name__ == "__main__": 195 | # 使用例 见data.json 批量多人完成 196 | with open("data.json", encoding="utf-8") as f: 197 | data_json = json.load(f) 198 | for nid_ in data_json.keys(): 199 | for stu in data_json[nid_]: 200 | stu_learn = YouthLearning(**stu, nid=nid_) 201 | stu_learn.main() 202 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 青春浙江-青年大学习打卡以及自动学习 2 | 3 | ## 快速使用 4 | 5 | 1、编辑main.py 6 | 7 | ```bash 8 | nid = "" #在这里输入你的团组织编号,形如N003************,请使用抓包软件获取 9 | cardNo = ""# 在这里输入你打卡时用的昵称,可能是学号,可能是姓名 10 | openid = "" #靠自己抓包 11 | nickname = "" #一般是微信账户名 12 | ``` 13 | 将以上内容填进main.py中 14 | 15 | 2、运行main.py 16 | ```bash 17 | python3 main.py 18 | ``` 19 | 其中python解释器的路径需根据自身情况设置 20 | 21 | ## 服务器端运行(推荐) 22 | 1、编辑main.py 23 | 24 | 2、添加crontab,并保存运行结果 25 | 26 | ```bash 27 | 25 6 * * * [python interpreter path] [source root path] >> [log location] 2>&1 28 | # eg: 25 6 * * * usr/local/bin/python3 ~/programs/QCZJ-Auotstudy-master/main.py >> ~/programs/QCZJ-Auotstudy-master/log.txt 2>&1 29 | ``` 30 | 将以上内容填进crontab中即可。 31 | 32 | ## 优化版 main2.py 33 | 34 | > 仅供参考学习,请勿用作其他用途,一律不对结果负责 35 | 36 | 代码更美观,便于批量完成多人任务 37 | 38 | 1、编辑data.json 39 | 40 | ```json 41 | { 42 | "组织编号1": [ 43 | { 44 | "name": "姓名1", 45 | "open_id": "open_id数据", 46 | "nick_name": "昵称1" 47 | }, 48 | { 49 | "name": "姓名2", 50 | "open_id": "open_id数据", 51 | "nick_name": "昵称2" 52 | } 53 | ], 54 | "组织编号2": [ 55 | { 56 | "name": "姓名3", 57 | "open_id": "open_id数据", 58 | "nick_name": "昵称3" 59 | }, 60 | { 61 | "name": "姓名4", 62 | "open_id": "open_id数据", 63 | "nick_name": "昵称4" 64 | } 65 | ] 66 | } 67 | ``` 68 | 69 | 2、运行main2.py 70 | 71 | ```bash 72 | > python3 main.py 73 | ``` 74 | 75 | ## To Do 76 | - [ ] 完善日志 77 | - [x] 重构代码 78 | - [x] 添加随机任务顺序 79 | - [ ] 增加推送功能 80 | - [x] 针对网络问题优化 81 | - [x] 不重复观看同一视频 82 | 83 | --------------------------------------------------------------------------------