├── .gitignore
├── AppShare
├── check_in.py
└── config.yaml
├── BiliBili
├── bilibili.py
└── bilibiliapi.py
├── CcpNoc
├── config.yaml
└── subject.py
├── ChinaTelecom
├── check_in.py
├── check_in_by_yz.py
└── live_by_yz.py
├── ChinaUnicom
└── read.py
├── LICENSE
├── MIUI
├── check_in.py
├── config.yaml
└── utils.py
├── QQSpeed
├── dig_treasure.py
├── jinsilou.py
└── sign.py
├── README.md
├── Utils
├── __init__.py
├── aes_encrypt.py
├── encrypt_symmetric.py
├── notify.py
├── rsa_encrypt.py
├── telecom_login.py
├── telecom_login_by_yz.py
├── tool.py
└── utils.py
├── YunMusic
└── check_in.py
└── requirements.txt
/.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 | .idea/
30 |
31 | # PyInstaller
32 | # Usually these files are written by a python script from a template
33 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
34 | *.manifest
35 | *.spec
36 |
37 | # Installer logs
38 | pip-log.txt
39 | pip-delete-this-directory.txt
40 |
41 | # Unit test / coverage reports
42 | htmlcov/
43 | .tox/
44 | .nox/
45 | .coverage
46 | .coverage.*
47 | .cache
48 | nosetests.xml
49 | coverage.xml
50 | *.cover
51 | *.py,cover
52 | .hypothesis/
53 | .pytest_cache/
54 |
55 | # Translations
56 | *.mo
57 | *.pot
58 |
59 | # Django stuff:
60 | *.log
61 | local_settings.py
62 | db.sqlite3
63 | db.sqlite3-journal
64 |
65 | # Flask stuff:
66 | instance/
67 | .webassets-cache
68 |
69 | # Scrapy stuff:
70 | .scrapy
71 |
72 | # Sphinx documentation
73 | docs/_build/
74 |
75 | # PyBuilder
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | .python-version
87 |
88 | # pipenv
89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
92 | # install all needed dependencies.
93 | #Pipfile.lock
94 |
95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
96 | __pypackages__/
97 |
98 | # Celery stuff
99 | celerybeat-schedule
100 | celerybeat.pid
101 |
102 | # SageMath parsed files
103 | *.sage.py
104 |
105 | # Environments
106 | .env
107 | .venv
108 | env/
109 | venv/
110 | ENV/
111 | env.bak/
112 | venv.bak/
113 |
114 | # Spyder project settings
115 | .spyderproject
116 | .spyproject
117 |
118 | # Rope project settings
119 | .ropeproject
120 |
121 | # mkdocs documentation
122 | /site
123 |
124 | # mypy
125 | .mypy_cache/
126 | .dmypy.json
127 | dmypy.json
128 |
129 | # Pyre type checker
130 | .pyre/
131 |
--------------------------------------------------------------------------------
/AppShare/check_in.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | """
4 | new Env('AppShare签到')
5 | cron: 0 8 0 * * *
6 | Author : SmallBaby
7 | Date : 2023-01-09 09:00:00
8 | LastEditTime : 2023-01-10 13:00:00
9 | FilePath : QingLongScript/AppShare/check_in.py
10 | Description : 脚本可进行AppShare自动签到,使用前请在配置文件添加账号信息(uid 和 password)
11 | """
12 |
13 | from datetime import datetime
14 | import requests
15 | import threading
16 | import sys
17 |
18 | sys.path.append('../')
19 | try:
20 | from Utils import utils, notify
21 | except Exception as err: # 异常捕捉
22 | print(f'{err}\n加载工具服务失败~\n')
23 |
24 | api = {
25 | 'login': '/v2/login',
26 | 'sign': '/user/v1/daySign',
27 | 'userInfo': '/user/v1/fragmentMeData',
28 | 'referToken': '/user/login/v1/launchApp2'
29 | }
30 |
31 |
32 | class AppShare:
33 | def __init__(self, userInfo):
34 | self.uid = userInfo.get('uid')
35 | self.password = utils.md5_crypto(userInfo.get('password'))
36 | self.oaid = utils.md5_crypto(userInfo.get('deviceId'))
37 | self.headers = get_header()
38 | self.userInfo = userInfo
39 | self.msg = f'账号"{self.uid}"信息:\n'
40 |
41 | def login(self):
42 | params = {
43 | 'oaid': self.oaid,
44 | 'account': self.uid,
45 | 'password': self.password,
46 | 'childDevice': 'false',
47 | }
48 | params['sign'] = get_sign(params)
49 | res = requests.get(f'{host}{api.get("login")}', params=params).json()
50 | isSuccess = res.get('code', 400) == 100
51 | msg = '登录成功\n' if isSuccess else '登录失败,请稍后重试\n'
52 | if isSuccess:
53 | self.get_info()
54 | self.msg += msg
55 | print(f'账号{self.uid}:{msg}')
56 | return isSuccess
57 |
58 | def get_info(self):
59 | params = {
60 | 'oaid': self.oaid
61 | }
62 | params['sign'] = get_sign(params)
63 | res = requests.get(f'{host}{api.get("userInfo")}', params=params).json()
64 | isSuccess = res.get('code', 400) == 100
65 | if isSuccess:
66 | self.userInfo['pushDeviceToken'] = res.get('data').get('userConfig').get('pushDeviceToken')
67 | print(f'账号{self.uid}信息:{res}')
68 |
69 | def refer_token(self):
70 | params = {
71 | 'oaid': self.oaid,
72 | 'versionCode': versionCode,
73 | 'deviceSdk': deviceSdk,
74 | 'pushDeviceToken': self.userInfo.get('pushDeviceToken'),
75 | }
76 | params['sign'] = get_sign(params)
77 | params.setdefault('deviceApi', deviceApi)
78 | res = requests.get(f'{host}{api.get("referToken")}', params=params).json()
79 | isSuccess = res.get('code', 400) == 100
80 | if isSuccess:
81 | self.get_info()
82 | msg = f'Token刷新: {res.get("message")}\n'
83 | self.msg += msg
84 | print(f'账号{self.uid}:{msg}')
85 | return isSuccess
86 |
87 | def check_in(self):
88 | params = {
89 | 'oaid': self.oaid,
90 | }
91 | params['sign'] = get_sign(params)
92 | res = requests.get(f'{host}{api.get("sign")}', params=params).json()
93 | msg = '签到失败,请稍后重试'
94 | if res.get('code', 400) == 100:
95 | data = res.get("data", {})
96 | msg = f'签到成功: {data.get("count")}\n'
97 | print(res)
98 | self.msg += msg
99 | print(f'账号{self.uid}:{msg}')
100 |
101 | def main(self):
102 | isSuccess = self.login() if self.userInfo.get('pushDeviceToken') is None else self.refer_token()
103 | if isSuccess:
104 | self.check_in()
105 | notify.send('AppShare消息推送', self.msg)
106 |
107 |
108 | # 获取header
109 | def get_header():
110 | headers = {
111 | 'accept-encoding': 'gzip',
112 | 'user-agent': 'okhttp/4.10.0'
113 | }
114 | return headers
115 |
116 |
117 | def get_sign(params):
118 | sign = ''
119 | datatime = datetime.now().strftime('%Y%m%d%H%M')
120 | for value in params.values():
121 | sign += str(value)
122 | sign += datatime
123 | return utils.md5_crypto(sign)
124 |
125 |
126 | # 生成随机deviceId
127 | def get_device_id():
128 | mask = '0123456789abcdefghijklmnopqrstuvwxyz'
129 | return utils.random_sign(mask, 16)
130 |
131 |
132 | def run_threading(userInfo):
133 | AppShare(userInfo).main()
134 |
135 |
136 | if __name__ == '__main__':
137 | threadList = []
138 | config = utils.read_yaml()
139 | print(f'当前配置版本: {config.get("version")}')
140 | host = config.get('host')
141 | deviceSdk = config.get('deviceSdk')
142 | deviceApi = config.get('deviceApi')
143 | versionCode = config.get('versionCode')
144 | userList = config.get('accounts')
145 | for user in userList:
146 | if user.get('deviceId') is None:
147 | user['deviceId'] = get_device_id()
148 | thread = threading.Thread(target=run_threading, args=(user,))
149 | threadList.append(thread)
150 | for thread in threadList:
151 | thread.start()
152 | thread.join()
153 | utils.write_yaml(config)
154 |
--------------------------------------------------------------------------------
/AppShare/config.yaml:
--------------------------------------------------------------------------------
1 | host: https://appshare.vip
2 | versionCode: sjBkCIqdMzm5fo5CDkWXHw%3D%3D
3 | deviceApi: 64
4 | deviceSdk: 30
5 | accounts:
6 | # 仅修改账号密码即可
7 | - deviceId: ~
8 | password: 123456
9 | pushDeviceToken: ~
10 | uid: admin@gmail.com
11 | # 多账号示例
12 | #- deviceId: ~
13 | # password: 123456
14 | # pushDeviceToken: ~
15 | # uid: admin@gmail.com
16 | version: v1.1.0
--------------------------------------------------------------------------------
/BiliBili/bilibili.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from bilibiliapi import *
3 | import Utils.notify
4 | import os
5 |
6 | sys.path.append('../')
7 |
8 | # 设置中国时区+8
9 | if os.environ['OS_TZ']:
10 | os.environ['TZ'] = 'UTC' + str(-int(os.environ['OS_TZ'])) + 'CN'
11 | time.tzset()
12 | else:
13 | os.environ['TZ'] = 'UTC-8CN'
14 | time.tzset()
15 |
16 | sendNotify = Utils.notify
17 | SEND_KEY = os.environ['SEND_KEY']
18 | BILI_COOKIE = os.environ['BILI_COOKIE'].replace(" ", "")
19 |
20 |
21 | class BiliBiliCheckIn(object):
22 | # 待测试,需要大会员账号测试领取福利
23 | def __init__(self, bilibili_cookie_list):
24 | self.bilibili_cookie_list = bilibili_cookie_list
25 |
26 | @staticmethod
27 | def get_nav(session):
28 | url = "https://api.bilibili.com/x/web-interface/nav"
29 | ret = session.get(url=url).json()
30 | # print(ret) #取消本段输出
31 | uname = ret.get("data", {}).get("uname")
32 | uid = ret.get("data", {}).get("mid")
33 | is_login = ret.get("data", {}).get("isLogin")
34 | coin = ret.get("data", {}).get("money")
35 | vip_type = ret.get("data", {}).get("vipType")
36 | current_exp = ret.get("data", {}).get("level_info", {}).get("current_exp")
37 | return uname, uid, is_login, coin, vip_type, current_exp
38 |
39 | @staticmethod
40 | def reward(session) -> dict:
41 | """取B站经验信息"""
42 | # url = "https://account.bilibili.com/home/reward"
43 | url = "https://api.bilibili.com/x/member/web/exp/reward"
44 | ret = session.get(url=url).json()
45 | return ret
46 |
47 | @staticmethod
48 | def coin_today_exp(session) -> dict:
49 | """取B站硬币经验信息"""
50 | url = "https://api.bilibili.com/x/web-interface/coin/today/exp"
51 | ret = session.get(url=url).json()
52 | return ret
53 |
54 | @staticmethod
55 | def vip_privilege_my(session) -> dict:
56 | """取B站大会员硬币经验信息"""
57 | url = "https://api.bilibili.com/x/vip/privilege/my"
58 | ret = session.get(url=url).json()
59 | return ret
60 |
61 | @staticmethod
62 | def live_sign(session) -> dict:
63 | """B站直播签到"""
64 | try:
65 | url = "https://api.live.bilibili.com/xlive/web-ucenter/v1/sign/DoSign"
66 | ret = session.get(url=url).json()
67 | if ret["code"] == 0:
68 | msg = f'签到成功,{ret["data"]["text"]},特别信息:{ret["data"]["specialText"]},本月已签到{ret["data"]["hadSignDays"]}天'
69 | elif ret["code"] == 1011040:
70 | msg = "今日已签到过,无法重复签到"
71 | else:
72 | msg = f'签到失败,信息为: {ret["message"]}'
73 | except Exception as e:
74 | msg = f"签到异常,原因为{str(e)}"
75 | return msg
76 |
77 | @staticmethod
78 | def manga_sign(session, platform="android") -> dict:
79 | """
80 | 模拟B站漫画客户端签到
81 | """
82 | try:
83 | url = "https://manga.bilibili.com/twirp/activity.v1.Activity/ClockIn"
84 | post_data = {"platform": platform}
85 | ret = session.post(url=url, data=post_data).json()
86 | if ret["code"] == 0:
87 | msg = "签到成功"
88 | elif ret["msg"] == "clockin clockin is duplicate":
89 | msg = "今天已经签到过了"
90 | else:
91 | msg = f'签到失败,信息为({ret["message"]})'
92 | except Exception as e:
93 | msg = f"签到异常,原因为: {str(e)}"
94 | return msg
95 |
96 | @staticmethod
97 | def vip_privilege_receive(session, bili_jct, receive_type: int = 1) -> dict:
98 | """
99 | 领取B站大会员权益
100 | receive_type int 权益类型,1为B币劵,2为优惠券
101 | """
102 | url = "https://api.bilibili.com/x/vip/privilege/receive"
103 | post_data = {"type": receive_type, "csrf": bili_jct}
104 | session.headers.update(
105 | {
106 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36",
107 | "Referer": "https://account.bilibili.com",
108 | "Connection": "keep-alive",
109 | "sec-fetch-mode": "cors",
110 | }
111 | )
112 | ret = session.post(url=url, data=post_data).json()
113 | return ret
114 |
115 | @staticmethod
116 | def vip_manga_reward(session) -> dict:
117 | """获取漫画大会员福利"""
118 | url = "https://manga.bilibili.com/twirp/user.v1.User/GetVipReward"
119 | ret = session.post(url=url, json={"reason_id": 1}).json()
120 | return ret
121 |
122 | @staticmethod
123 | def report_task(session, bili_jct, aid: int, cid: int, progres: int = 300) -> dict:
124 | """
125 | B站上报视频观看进度
126 | aid int 视频av号
127 | cid int 视频cid号
128 | progres int 观看秒数
129 | """
130 | url = "http://api.bilibili.com/x/v2/history/report"
131 | post_data = {"aid": aid, "cid": cid, "progres": progres, "csrf": bili_jct}
132 | ret = session.post(url=url, data=post_data).json()
133 | return ret
134 |
135 | @staticmethod
136 | def share_task(session, bili_jct, aid) -> dict:
137 | """
138 | 分享指定av号视频
139 | aid int 视频av号
140 | """
141 | url = "https://api.bilibili.com/x/web-interface/share/add"
142 | post_data = {"aid": aid, "csrf": bili_jct}
143 | ret = session.post(url=url, data=post_data).json()
144 | return ret
145 |
146 | @staticmethod
147 | def get_followings(
148 | session, uid: int, pn: int = 1, ps: int = 50, order: str = "desc", order_type: str = "attention"
149 | ) -> dict:
150 | """
151 | 获取指定用户关注的up主
152 | uid int 账户uid,默认为本账户,非登录账户只能获取20个*5页
153 | pn int 页码,默认第一页
154 | ps int 每页数量,默认50
155 | order str 排序方式,默认desc
156 | order_type 排序类型,默认attention
157 | """
158 | params = {
159 | "vmid": uid,
160 | "pn": pn,
161 | "ps": ps,
162 | "order": order,
163 | "order_type": order_type,
164 | }
165 | url = f"https://api.bilibili.com/x/relation/followings"
166 | ret = session.get(url=url, params=params).json()
167 | return ret
168 |
169 | @staticmethod
170 | def space_arc_search(
171 | session, uid: int, pn: int = 1, ps: int = 100, tid: int = 0, order: str = "pubdate", keyword: str = ""
172 | ) -> dict:
173 | """
174 | 获取指定up主空间视频投稿信息
175 | uid int 账户uid,默认为本账户
176 | pn int 页码,默认第一页
177 | ps int 每页数量,默认50
178 | tid int 分区 默认为0(所有分区)
179 | order str 排序方式,默认pubdate
180 | keyword str 关键字,默认为空
181 | """
182 | params = {
183 | "mid": uid,
184 | "pn": pn,
185 | "ps": ps,
186 | "tid": tid,
187 | "order": order,
188 | "keyword": keyword,
189 | }
190 | url = f"https://api.bilibili.com/x/space/arc/search"
191 | ret = session.get(url=url, params=params).json()
192 | data_list = [
193 | {"aid": one.get("aid"), "cid": 0, "title": one.get("title"), "owner": one.get("author")}
194 | for one in ret.get("data", {}).get("list", {}).get("vlist", [])
195 | ]
196 | return data_list
197 |
198 | @staticmethod
199 | def elec_pay(session, bili_jct, uid: int, num: int = 50) -> dict:
200 | """
201 | 用B币给up主充电
202 | uid int up主uid
203 | num int 充电电池数量
204 | """
205 | url = "https://api.bilibili.com/x/ugcpay/trade/elec/pay/quick"
206 | post_data = {"elec_num": num, "up_mid": uid, "otype": "up", "oid": uid, "csrf": bili_jct}
207 | ret = session.post(url=url, data=post_data).json()
208 | return ret
209 |
210 | @staticmethod
211 | def coin_add(session, bili_jct, aid: int, num: int = 1, select_like: int = 1) -> dict:
212 | """
213 | 给指定 av 号视频投币
214 | aid int 视频av号
215 | num int 投币数量
216 | select_like int 是否点赞
217 | """
218 | url = "https://api.bilibili.com/x/web-interface/coin/add"
219 | post_data = {
220 | "aid": aid,
221 | "multiply": num,
222 | "select_like": select_like,
223 | "cross_domain": "true",
224 | "csrf": bili_jct,
225 | }
226 | ret = session.post(url=url, data=post_data).json()
227 |
228 | return ret
229 |
230 | @staticmethod
231 | def live_status(session) -> dict:
232 | """B站直播获取金银瓜子状态"""
233 | url = "https://api.live.bilibili.com/pay/v1/Exchange/getStatus"
234 | ret = session.get(url=url).json()
235 | data = ret.get("data")
236 | silver = data.get("silver", 0)
237 | gold = data.get("gold", 0)
238 | coin = data.get("coin", 0)
239 | msg = f"银瓜子数量: {silver}\n金瓜子数量: {gold}\n硬币数量: {coin}"
240 | return msg
241 |
242 | @staticmethod
243 | def silver2coin(session, bili_jct) -> dict:
244 | """银瓜子兑换硬币"""
245 | url = "https://api.live.bilibili.com/pay/v1/Exchange/silver2coin"
246 | post_data = {"csrf_token": bili_jct}
247 | ret = session.post(url=url, data=post_data).json()
248 | return ret
249 |
250 | @staticmethod
251 | def get_region(session, rid=1, num=6) -> dict:
252 | """
253 | 获取 B站分区视频信息
254 | rid int 分区号
255 | num int 获取视频数量
256 | """
257 | url = "https://api.bilibili.com/x/web-interface/dynamic/region?ps=" + str(num) + "&rid=" + str(rid)
258 | ret = session.get(url=url).json()
259 | data_list = [
260 | {
261 | "aid": one.get("aid"),
262 | "cid": one.get("cid"),
263 | "title": one.get("title"),
264 | "owner": one.get("owner", {}).get("name"),
265 | }
266 | for one in ret.get("data", {}).get("archives", [])
267 | ]
268 | return data_list
269 |
270 | def main(self):
271 | msg_list = []
272 | bilibili_cookie = self.bilibili_cookie_list
273 | bili_jct = bilibili_cookie.get("bili_jct")
274 |
275 | if os.environ['BILI_NUM'] == "":
276 | coin_num = 0 # 投币数量
277 | else:
278 | coin_num = int(os.environ['BILI_NUM'])
279 |
280 | if os.environ['BILI_TYPE'] == "":
281 | coin_type = 1 # 投币方式 默认为 1 ;1: 为关注用户列表视频投币 0: 为随机投币。如果关注用户发布的视频不足配置的投币数,则剩余部分使用随机投币
282 | else:
283 | coin_type = int(os.environ['BILI_TYPE'])
284 |
285 | silver2coin = True # 是否开启银瓜子换硬币,默认为 True 开启
286 | session = requests.session()
287 | requests.utils.add_dict_to_cookiejar(session.cookies, bilibili_cookie)
288 | session.headers.update(
289 | {
290 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36",
291 | "Referer": "https://account.bilibili.com",
292 | "Connection": "keep-alive",
293 | }
294 | )
295 | success_count = 0
296 | uname, uid, is_login, coin, vip_type, current_exp = self.get_nav(session=session)
297 | # print(uname, uid, is_login, coin, vip_type, current_exp)
298 | if is_login:
299 | manhua_msg = self.manga_sign(session=session)
300 | print(manhua_msg)
301 | live_msg = self.live_sign(session=session)
302 | print(live_msg)
303 | aid_list = self.get_region(session=session)
304 | vip_privilege_my_ret = self.vip_privilege_my(session=session)
305 | welfare_list = vip_privilege_my_ret.get("data", {}).get("list", [])
306 | for welfare in welfare_list:
307 | if welfare.get("state") == 0 and welfare.get("vip_type") == vip_type:
308 | vip_privilege_receive_ret = self.vip_privilege_receive(session=session, bili_jct=bili_jct,
309 | receive_type=welfare.get("type"))
310 | print(vip_privilege_receive_ret) # 取消本段输出
311 | reward_ret = self.reward(session=session)
312 | # print(reward_ret) # 取消本段输出
313 | coins_av_count = reward_ret.get("data", {}).get("coins") // 10
314 | coin_num = coin_num - coins_av_count
315 | coin_num = coin_num if coin_num < coin else coin
316 | print(coin_num)
317 | if coin_type == 1 and coin_num:
318 | following_list = self.get_followings(session=session, uid=uid)
319 | for following in following_list.get("data", {}).get("list"):
320 | mid = following.get("mid")
321 | if mid:
322 | aid_list += self.space_arc_search(session=session, uid=mid)
323 | if coin_num > 0:
324 | for aid in aid_list[::-1]:
325 | # print(f'成功给{aid.get("title")}投一个币')
326 | # coin_num -= 1
327 | # success_count += 1
328 | ret = self.coin_add(session=session, aid=aid.get("aid"), bili_jct=bili_jct)
329 | if ret["code"] == 0:
330 | coin_num -= 1
331 | print(f'成功给{aid.get("title")}投一个币')
332 | success_count += 1
333 | elif ret["code"] == 34005:
334 | print(f'投币{aid.get("title")}失败,原因为{ret["message"]}')
335 | continue
336 | # -104 硬币不够了 -111 csrf 失败 34005 投币达到上限
337 | else:
338 | print(f'投币{aid.get("title")}失败,原因为{ret["message"]},跳过投币')
339 | break
340 | if coin_num <= 0:
341 | break
342 | coin_msg = f"今日成功投币{success_count + coins_av_count}/{coin_num}个"
343 | print(coin_msg)
344 | else:
345 | coin_msg = f"今日成功投币{coins_av_count}/{coin_type}个"
346 | print(coin_msg)
347 | aid = aid_list[0].get("aid")
348 | cid = aid_list[0].get("cid")
349 | title = aid_list[0].get("title")
350 | report_ret = self.report_task(session=session, bili_jct=bili_jct, aid=aid, cid=cid)
351 | if report_ret.get("code") == 0:
352 | report_msg = f"观看《{title}》300秒"
353 | else:
354 | report_msg = f"任务失败"
355 | print(report_msg)
356 | share_ret = self.share_task(session=session, bili_jct=bili_jct, aid=aid)
357 | if share_ret.get("code") == 0:
358 | share_msg = f"分享《{title}》成功"
359 | else:
360 | share_msg = f"分享失败"
361 | print(share_msg)
362 | if silver2coin:
363 | silver2coin_ret = self.silver2coin(session=session, bili_jct=bili_jct)
364 | if silver2coin_ret["code"] == 0:
365 | silver2coin_msg = f"成功将银瓜子兑换为1个硬币"
366 | else:
367 | silver2coin_msg = silver2coin_ret["message"]
368 | print(silver2coin_msg)
369 | else:
370 | silver2coin_msg = f"未开启银瓜子兑换硬币功能"
371 | live_stats = self.live_status(session=session)
372 | uname, uid, is_login, new_coin, vip_type, new_current_exp = self.get_nav(session=session)
373 | # print(uname, uid, is_login, new_coin, vip_type, new_current_exp)
374 | reward_ret = self.reward(session=session)
375 | login = reward_ret.get("data", {}).get("login")
376 | watch_av = reward_ret.get("data", {}).get("watch")
377 | coins_av = reward_ret.get("data", {}).get("coins", 0)
378 | share_av = reward_ret.get("data", {}).get("share")
379 | today_exp = len([one for one in [login, watch_av, share_av] if one]) * 5
380 | today_exp += coins_av
381 | update_data = (28800 - new_current_exp) // (today_exp if today_exp else 1)
382 | msg = (
383 | f"【Bilibili签到】\n帐号信息: {uname}\n漫画签到: {manhua_msg}\n直播签到: {live_msg}\n"
384 | f"登陆任务: 今日已登陆\n观看视频: {report_msg}\n分享任务: {share_msg}\n投币任务: {coin_msg}\n"
385 | f"银瓜子兑换硬币: {silver2coin_msg}\n今日获得经验: {today_exp}\n当前经验: {new_current_exp}\n"
386 | f"按当前速度升级还需: {update_data}天\n{live_stats}"
387 | )
388 | print(msg)
389 | if SEND_KEY == '':
390 | sendNotify.send(title=u"哔哩哔哩签到", msg=msg)
391 | msg_list.append(msg)
392 | else:
393 | if SEND_KEY == '':
394 | sendNotify.send(title=u"哔哩哔哩签到", msg="cookie已过期,请更新")
395 | return msg_list
396 |
397 |
398 | if __name__ == "__main__":
399 | # 未填写参数取消运行
400 | if os.environ['BILI_USER'] == "" or os.environ['BILI_PASS'] == "":
401 | if os.environ['BILI_COOKIE'] == "":
402 | print("未填写哔哩哔哩账号密码或COOKIE取消运行")
403 | exit(0)
404 |
405 | if BILI_COOKIE == "":
406 | b = Bilibili()
407 | login = b.login(username=os.environ['BILI_USER'], password=os.environ['BILI_PASS'])
408 | if not login:
409 | sendNotify.send(title=u"哔哩哔哩签到", msg="登录失败 账号或密码错误,详情前往Github查看")
410 | exit(0)
411 | _bilibili_cookie_list = b.get_cookies()
412 | else:
413 | _bilibili_cookie_list = {cookie.split('=')[0]: cookie.split('=')[-1] for cookie in BILI_COOKIE.split(';')}
414 |
415 | BiliBiliCheckIn(bilibili_cookie_list=_bilibili_cookie_list).main()
416 |
--------------------------------------------------------------------------------
/CcpNoc/config.yaml:
--------------------------------------------------------------------------------
1 | host: http://ccp.noc.net.cn
2 | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
3 | accounts:
4 | - phone: 13388888888
5 | password: CcpNoc
6 | version: v1.0.0
7 |
--------------------------------------------------------------------------------
/CcpNoc/subject.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | """
4 | new Env('CCP NOC指导教师刷课')
5 | cron: 0 8-22 0 * * *
6 | Author : SmallBaby
7 | Date : 2023-02-11 09:00:00
8 | LastEditTime : 2023-02-15 16:00:00
9 | FilePath : QingLongScript/CcpNoc/subject.py
10 | Description : 脚本可进行NOC指导教师刷课,使用前请在配置文件添加账号信息(phone 和 password)
11 | """
12 | from bs4 import BeautifulSoup
13 | import threading
14 | import requests
15 | import sys
16 |
17 | sys.path.append('../')
18 | try:
19 | from Utils import utils
20 | except Exception as err: # 异常捕捉
21 | print(f'{err}\n加载工具服务失败~\n')
22 |
23 |
24 | def get_header():
25 | return {
26 | 'accept': 'application/json, text/javascript, */*; q=0.01',
27 | 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
28 | 'cache-control': 'no-cache',
29 | 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
30 | 'pragma': 'no-cache',
31 | 'x-requested-with': 'XMLHttpRequest',
32 | 'user-agent': userAgent
33 | }
34 |
35 |
36 | def format_time(timeStr):
37 | [h, m] = timeStr.split(':')
38 | return int(h) * 3600 + int(m) * 60
39 |
40 |
41 | class Noc:
42 | def __init__(self, idcard, pwd):
43 | self.idcard = idcard
44 | self.pwd = pwd
45 | self.headers = get_header()
46 | self.get_cookie()
47 | self.courseList = self.get_user_course()
48 |
49 | def get_cookie(self):
50 | cookie = self.do_login()
51 | self.headers['cookie'] = cookie
52 |
53 | def do_login(self):
54 | self.headers['referrer'] = f'{host}/Home/Login'
55 | data = {
56 | 'idcard': self.idcard,
57 | 'pwd': self.pwd
58 | }
59 | res = requests.post(f'{host}/Home/SubmitLogin', headers=self.headers, data=data)
60 | isSuccess = res.json().get('Result')
61 | if isSuccess:
62 | return res.headers.get('Set-Cookie').split(';', 1)[0]
63 | else:
64 | print(f'账号{self.idcard}登录失败:{res.json().get("Msg")},程序退出!')
65 | exit(0)
66 |
67 | def get_user_course(self, page=1, size=9):
68 | self.headers['referrer'] = f'{host}/User/Course'
69 | data = {
70 | 'page': page,
71 | 'size': size
72 | }
73 | res = requests.post(f'{host}/User/GetMemberCoursePage', headers=self.headers, data=data).json()
74 | exCourseList = res.get('Data')[0].get('ExMemberCourseList')
75 | return exCourseList if len(exCourseList) > 0 else []
76 |
77 | def send_lesson(self, lesson, params):
78 | self.headers['referrer'] = f'{host}/Course/Lesson?cid={params.get("cid")}&lid={params.get("lid")}'
79 | data = {
80 | 'lesson': lesson
81 | }
82 | res = requests.post(f'{host}/Course/Lessoned', headers=self.headers, data=data)
83 | if res.status_code == 200:
84 | print('初始化完成' if lesson == 0 else '上报完成')
85 | return True
86 | else:
87 | print(f'send_lesson: 请求失败, lesson: {lesson}')
88 | return False
89 |
90 | def send_increase_duration(self, params):
91 | self.headers['referrer'] = f'{host}/Course/Lesson?cid={params.get("cid")}&lid={params.get("lid")}'
92 | data = {
93 | 'id': int(params.get('lid')),
94 | 'duration': 10
95 | }
96 | res = requests.post(f'{host}/Course/IncreaseDuration', headers=self.headers, data=data)
97 | if res.status_code == 200:
98 | print(f'{params.get("name")}视频观看10s, 上报完成')
99 | return True
100 | else:
101 | print(f'send_increase_duration: 请求失败')
102 | return False
103 |
104 | def get_course_index(self, cid):
105 | self.headers['referrer'] = f'{host}/User/Course'
106 | params = {'id': cid}
107 | res = requests.get(f'{host}/Course/Index', headers=self.headers, params=params)
108 | htmlStr = res.text
109 | html = BeautifulSoup(htmlStr, 'html.parser')
110 | subjectList = []
111 | for target in html.select_one('.catList').select('a'):
112 | href = target.get('href')
113 | if 'Lesson' in href and 'cid' in href and 'lid' in href:
114 | subObj = {
115 | **utils.format_params(href),
116 | 'name': target.select('div > span')[0].text.strip(),
117 | 'percent': int(target.select_one('.layui-progress-bar').get('lay-percent').replace('%', '')),
118 | 'subTime': format_time(target.select('div > span')[1].text)
119 | }
120 | subjectList.append(subObj)
121 |
122 | return subjectList if len(subjectList) > 0 else []
123 |
124 | def main(self):
125 | for subject in self.courseList:
126 | if subject.get('ExProgress') == 100:
127 | print(f'{subject.get("ExCcpCourseName")}进度已完成,跳过')
128 | continue
129 | print(f'{subject.get("ExCcpCourseName")}进度{subject.get("ExProgress")}%,开始刷课')
130 | videoList = self.get_course_index(subject.get('CcpCourseID'))
131 | for node in videoList:
132 | if node.get('percent') == 100:
133 | print(f'{subject.get("ExCcpCourseName")}中<{node.get("name")}>进度已完成,跳过')
134 | if self.send_lesson(0, node):
135 | lookTime = node.get('subTime') * node.get('percent') / 100
136 | while node.get('subTime') > lookTime:
137 | threads = []
138 | remainingTime = node.get('subTime') - lookTime
139 | rateTime = 10 if remainingTime >= 10 else remainingTime
140 | lessonThread = threading.Timer(8, self.send_lesson, args=(1, node))
141 | increaseThread = threading.Timer(rateTime, self.send_increase_duration, args=(node,))
142 | threads.append(lessonThread)
143 | threads.append(increaseThread)
144 | for t in threads:
145 | t.start()
146 | t.join()
147 | lookTime += rateTime
148 |
149 |
150 | def run_threading(phone, password):
151 | Noc(phone, password).main()
152 |
153 |
154 | if __name__ == '__main__':
155 | threadList = []
156 | config = utils.read_yaml()
157 | print(f'当前配置版本: {config.get("version")}')
158 | host = config.get('host')
159 | userAgent = config.get('userAgent')
160 | userList = config.get('accounts')
161 | for user in userList:
162 | thread = threading.Thread(target=run_threading, kwargs=user)
163 | threadList.append(thread)
164 | for thread in threadList:
165 | thread.start()
166 | thread.join()
167 |
--------------------------------------------------------------------------------
/ChinaTelecom/check_in.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | from time import sleep, time
4 | from datetime import date, datetime
5 | from random import choices
6 | from requests import get, post
7 | from binascii import b2a_hex, a2b_hex
8 | from base64 import b64encode, b64decode
9 | from Crypto.PublicKey.RSA import importKey, construct
10 | from Crypto.Cipher import PKCS1_v1_5
11 | from Crypto.Cipher import AES
12 | from string import ascii_letters, digits
13 | from Utils.tool import get_environ
14 | import os
15 |
16 | try:
17 | import Utils.notify
18 | except Exception as err: # 异常捕捉
19 | print(f'{err}\n加载通知服务失败~\n')
20 |
21 | # 签到喂食只需要手机号码,多号填写多行,去掉开头#号
22 | account_list = get_environ("TELECOM_PHONE").split(",")
23 |
24 | # 是否发送青龙通知
25 | sendNotify = True
26 | # 收集通知内容
27 | msgg = []
28 | # 通知的标题
29 | title = '中国电信签到喂食'
30 |
31 |
32 | def checkCount():
33 | try:
34 | strTime = datetime.now().__format__("%Y-%m-%d")
35 | filename = os.path.basename(__file__)[:-3]
36 | path = "../log/" + filename
37 | if os.path.exists(path):
38 | listDir = os.listdir(path)
39 | sum_sign = 0
40 | sum_food = 0
41 | for item in listDir:
42 | if (item.startswith(strTime)):
43 | filePath = os.path.join(path, item)
44 | with open(filePath, 'r', encoding='utf8') as f:
45 | txt = f.read()
46 | food = txt.count("今天已达到最大喂食次数")
47 | sign = txt.count("签到成功")
48 | if (sign > 0 or food > 0):
49 | sum_sign += sign
50 | sum_food += food
51 | msgg.append("参考日志文件:{0}".format(item))
52 | msgg.append("签到完成{0}次,最大喂食{1}次".format(sign, food))
53 | if (sum_sign == 0 and sum_food == 0):
54 | return False
55 |
56 | x = len(account_list)
57 | msgg.append('----------------------------------------')
58 | msgg.append("签到总数量{0}个,喂食总数量{1}个".format(x, x))
59 | msgg.append("签到完成总次数{0}次,喂食成功总次数{1}次".format(sum_sign, sum_food))
60 | if sum_sign >= x and sum_food >= x:
61 | msgg.append("达标! 取消发送签到请求!")
62 | return True
63 | else:
64 | msgg.append("未达标! 继续发送签到请求!")
65 | return False
66 | else:
67 | msgg.append(f"文件夹不存在{path}")
68 | return False
69 | except Exception as e:
70 | print(e)
71 | finally:
72 | msgg.append(f'---------------')
73 |
74 |
75 | def timestamp(short=False):
76 | if short:
77 | return int(round(time()))
78 | return int(round(time() * 1000))
79 |
80 |
81 | class AES_Ctypt:
82 | def __init__(self, key, iv=None, mode="ECB"):
83 | if len(key) % 16 != 0:
84 | key = key + (AES.block_size - len(key) % AES.block_size) * chr(0)
85 | self.key = key.encode("utf-8")
86 | if mode == "ECB":
87 | self.mode = AES.MODE_ECB
88 | elif mode == "CBC":
89 | self.mode = AES.MODE_CBC
90 | else:
91 | print("您选择的加密方式错误")
92 | if iv is None:
93 | self.cipher = AES.new(self.key, self.mode)
94 | else:
95 | if isinstance(iv, str):
96 | self.cipher = AES.new(self.key, self.mode, iv.encode("utf-8"))
97 | else:
98 | print("偏移量不为字符串")
99 |
100 | def encrypt(self, data, padding="pkcs7", b64=False):
101 | bs = AES.block_size
102 | pkcs7_padding = lambda s: s + (bs - len(s.encode()) % bs) * chr(bs - len(s.encode()) % bs)
103 | zero_padding = lambda s: s + (bs - len(s) % bs) * chr(0)
104 | pad = pkcs7_padding if padding == "pkcs7" else zero_padding
105 | data = self.cipher.encrypt(pad(data).encode("utf8"))
106 | encrypt_data = b64encode(data) if b64 else b2a_hex(data) # 输出hex或者base64
107 | return encrypt_data.decode('utf8')
108 |
109 | def decrypt(self, data, b64=False):
110 | data = b64decode(data) if b64 else a2b_hex(data)
111 | decrypt_data = self.cipher.decrypt(data).decode()
112 | unpadding = lambda s: s.replace(s[-1], "")
113 | return unpadding(decrypt_data)
114 |
115 |
116 | class RSA_Encrypt:
117 | def __init__(self, key):
118 | if isinstance(key, str):
119 | self.key = self.public_key(key) if "PUBLIC KEY" not in key else key.encode()
120 | else:
121 | print("提供的公钥格式不正确")
122 |
123 | def public_key(self, rsaExponent, rsaModulus=10001):
124 | e = int(rsaExponent, 16)
125 | n = int(rsaModulus, 16) # snipped for brevity
126 | pubkey = construct((n, e)).export_key()
127 | return pubkey
128 |
129 | def encrypt(self, data, b64=False):
130 | pub_key = importKey(self.key)
131 | cipher = PKCS1_v1_5.new(pub_key)
132 | rsa_text = cipher.encrypt(data.encode("utf8"))
133 | rsa_text = b64encode(rsa_text).decode() if b64 else rsa_text.hex()
134 | return rsa_text
135 |
136 |
137 | def get_phoneNum(phone):
138 | result = ""
139 | for i in phone:
140 | result += chr(ord(i) + 2)
141 | return result
142 |
143 |
144 | def add(txt):
145 | return a2b_hex(''.join(['2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d4947664d',
146 | '413047435371475349623344514542415155414134474e4144434269514b4267',
147 | '51432b756747354138635a334671554b44774d3537474d34696f360a4a476353',
148 | '746976543855644774363750454f69684c5a54773350373337312b4e34375072',
149 | '6d7343706e54527a6254676375704b74557638496d5a616c596b36350a645538',
150 | '726a432f72696477687739666657324c4277766b456e446b6b4b4b5269326c69',
151 | '574949744466744a564269574f6831376f36676662506f4e72574f52630a4164',
152 | '6362706b324c2b75646c64356b5a4e774944415141420a2d2d2d2d2d454e4420',
153 | '5055424c4943204b45592d2d2d2d2d'])).decode()
154 |
155 |
156 | class ChinaTelecom:
157 | def __init__(self, account):
158 | self.phone = account
159 |
160 | def init(self):
161 | self.ua = f"CtClient;9.6.1;Android;12;SM-G9860;{b64encode(self.phone[5:11].encode()).decode().strip('=+')}!#!{b64encode(self.phone[0:5].encode()).decode().strip('=+')}"
162 | self.headers = {
163 | "Host": "wapside.189.cn:9001",
164 | "Referer": "https://wapside.189.cn:9001/resources/dist/signInActivity.html",
165 | "User-Agent": self.ua
166 | }
167 |
168 | def req(self, url, method, data=None):
169 | retryTimes = 0
170 | while retryTimes < 5:
171 | try:
172 | if method == "GET":
173 | return get(url, headers=self.headers).json()
174 | elif method.upper() == "POST":
175 | return post(url, headers=self.headers, json=data).json()
176 | else:
177 | print("您当前使用的请求方式有误,请检查")
178 | return None
179 | except Exception as e:
180 | retryTimes += 1
181 | print(f'{method} ,请求异常,尝试重新第{retryTimes}次请求:{e}')
182 | return None
183 |
184 | def telecom(self, text):
185 | if len(text) <= 32:
186 | return RSA_Encrypt(add(text)).encrypt(text)
187 | else:
188 | encrypt_text = ""
189 | for i in range(int(len(text) / 32) + 1):
190 | split_text = text[(32 * i):(32 * (i + 1))]
191 | encrypt_text += RSA_Encrypt(add(text)).encrypt(split_text)
192 | return encrypt_text
193 |
194 | @staticmethod
195 | def geneRandomToken():
196 | randomList = choices(ascii_letters + digits, k=129)
197 | token = f"V1.0{''.join(x for x in randomList)}"
198 | return token
199 |
200 | def check_in(self):
201 | try:
202 | url = "https://wapside.189.cn:9001/jt-sign/api/home/sign"
203 | data = {
204 | "encode": AES_Ctypt("34d7cb0bcdf07523").encrypt(
205 | f'{{"phone":{self.phone},"date":{timestamp()},"signSource":"smlprgrm"}}')
206 | }
207 | jsont = self.req(url, "post", data)
208 | if jsont == None:
209 | return
210 | else:
211 | if jsont["data"]["code"] == 1:
212 | print(f'签到结果:{jsont["data"]["msg"]},获得{jsont["data"]["coin"]}金豆,已签到{jsont["data"]["totalDay"]}天')
213 | msgg.append(
214 | f'签到结果:{jsont["data"]["msg"]},获得{jsont["data"]["coin"]}金豆,已签到{jsont["data"]["totalDay"]}天')
215 | else:
216 | print(f'签到异常:{jsont["data"]["msg"]}')
217 | msgg.append(f'签到异常:{jsont["data"]["msg"]}')
218 | except Exception as e:
219 | raise Exception(f'签到,{e}')
220 |
221 | def get_task(self):
222 | try:
223 | url = "https://wapside.189.cn:9001/jt-sign/paradise/getTask"
224 | data = {
225 | "para": self.telecom(f'{{"phone":{self.phone}}}')
226 | }
227 | jsont = self.req(url, "post", data)
228 | if jsont == None:
229 | return
230 | if jsont["resoultCode"] == "0":
231 | self.task_list = jsont["data"]
232 | else:
233 | print("获取任务列表失败")
234 | print(jsont)
235 | except Exception as e:
236 | raise Exception(f'获取任务列表,{e}')
237 |
238 | def do_task(self):
239 | try:
240 | url = "https://wapside.189.cn:9001/jt-sign/paradise/polymerize"
241 | for task in self.task_list:
242 | if "翻牌抽好礼" in task["title"] or "查看我的订单" in task["title"] or "查看我的云盘" in task["title"]:
243 | decrept_para = f'{{"phone":"{self.phone}","jobId":"{task["taskId"]}"}}'
244 | data = {"para": self.telecom(decrept_para)}
245 | jsont = self.req(url, "POST", data)
246 | if jsont == None:
247 | return
248 | if jsont["data"]["code"] == 0:
249 | print(
250 | f'每日任务:{task["title"].split(" ")[0]},{jsont["resoultMsg"]},{task["title"].split(" ", 1)[1]}')
251 | msgg.append(f'每日任务:{task["title"].split(" ")[0]},{task["title"].split(" ", 1)[1]}')
252 | else:
253 | print(f'每日任务:{task["title"].split(" ")[0]},{jsont["resoultMsg"]}')
254 | msgg.append(f'每日任务:{task["title"].split(" ")[0]},{jsont["resoultMsg"]}')
255 | except Exception as e:
256 | raise Exception(f'每日任务,{e}')
257 |
258 | def food(self):
259 | try:
260 | url = "https://wapside.189.cn:9001/jt-sign/paradise/food"
261 | data = {
262 | "para": self.telecom(f'{{"phone":{self.phone}}}')
263 | }
264 | jsont = self.req(url, "POST", data)
265 | if jsont == None:
266 | return
267 | if jsont["resoultCode"] == "0":
268 | print(f'喂食结果:{jsont["resoultMsg"]}')
269 | msgg.append(f'喂食结果:{jsont["resoultMsg"]}')
270 | else:
271 | print(f'喂食异常:{jsont["resoultMsg"]}')
272 | msgg.append(f'喂食异常:{jsont["resoultMsg"]}')
273 | return jsont["resoultMsg"]
274 | except Exception as e:
275 | raise Exception(f'宠物喂食,{e}')
276 |
277 | def get_level2(self):
278 | try:
279 | url = "https://wapside.189.cn:9001/jt-sign/paradise/getParadiseInfo"
280 | body = {
281 | "para": self.telecom(f'{{"phone":{self.phone}}}')
282 | }
283 | data = self.req(url, "POST", body)
284 | if int(data["userInfo"]["paradiseDressup"]["level"]) >= 6:
285 | print(f'当前等级:Lv{data["userInfo"]["paradiseDressup"]["level"]}, 已达到满级!')
286 | msgg.append(f'当前等级:Lv{data["userInfo"]["paradiseDressup"]["level"]}, 已达到满级!')
287 | else:
288 | print(f'当前等级:Lv{data["userInfo"]["paradiseDressup"]["level"]}, {data["percentage"]}')
289 | msgg.append(f'当前等级:Lv{data["userInfo"]["paradiseDressup"]["level"]}, {data["percentage"]}')
290 | except Exception as e:
291 | raise Exception(f'宠物等级,{e}')
292 |
293 | def coin_info(self):
294 | try:
295 | url = "https://wapside.189.cn:9001/jt-sign/api/home/userCoinInfo"
296 | data = {
297 | "para": self.telecom(f'{{"phone":{self.phone}}}')
298 | }
299 | jsont = self.req(url, "post", data)
300 | if jsont == None:
301 | return
302 | if jsont["resoultCode"] == '0':
303 | self.coin_count = jsont
304 | else:
305 | self.coin_count = {"totalCoin": None}
306 | except Exception as e:
307 | raise Exception(f'查询金豆数量,{e}')
308 |
309 | def get_userid(self):
310 | url = "https://wapside.189.cn:9001/jt-sign/api/home/homeInfo"
311 | body = {
312 | "para": self.telecom(f'{{"phone":"{self.phone}","signDate":"{datetime.now().__format__("%Y-%m")}"}}')
313 | }
314 | userid = post(url, json=body).json()["data"]["userInfo"]["userThirdId"]
315 | return userid
316 |
317 | def share(self):
318 | try:
319 | url = "https://appfuwu.189.cn:9021/query/sharingGetGold"
320 | body = {
321 | "headerInfos": {
322 | "code": "sharingGetGold",
323 | "timestamp": datetime.now().__format__("%Y%m%d%H%M%S"),
324 | "broadAccount": "",
325 | "broadToken": "",
326 | "clientType": "#9.6.1#channel50#iPhone 14 Pro Max#",
327 | "shopId": "20002",
328 | "source": "110003",
329 | "sourcePassword": "Sid98s",
330 | "token": self.geneRandomToken(),
331 | "userLoginName": self.phone
332 | },
333 | "content": {
334 | "attach": "test",
335 | "fieldData": {
336 | "shareSource": "3",
337 | "userId": self.get_userid(),
338 | "account": get_phoneNum(self.phone)
339 | }
340 | }
341 | }
342 | headers = {
343 | "user-agent": "iPhone 14 Pro Max/9.6.1"
344 | }
345 | data = post(url, headers=headers, json=body).json()
346 | print(f'分享任务:{data["responseData"]["resultDesc"]}')
347 | msgg.append(f'分享任务:{data["responseData"]["resultDesc"]}')
348 | except Exception as e:
349 | raise Exception(f'分享任务,{e}')
350 |
351 | def main(self):
352 | try:
353 | self.init()
354 | self.coin_info()
355 | print(f'手机号码:{self.phone.replace(self.phone[3:7], "****")}')
356 | msgg.append(f'手机号码:{self.phone.replace(self.phone[3:7], "****")}')
357 | print(f'当前金豆:{self.coin_count["totalCoin"]}')
358 | msgg.append(f'当前金豆:{self.coin_count["totalCoin"]}')
359 | # print('----签到----')
360 | self.check_in()
361 | # print('----查询等级----')
362 | self.get_level2()
363 | # print('----喂食----')
364 | for i in range(20):
365 | ret = self.food()
366 | if ret.find('已达到最大喂食次数') != -1:
367 | break
368 | # print('----做任务----')
369 | self.get_task()
370 | self.do_task()
371 | # print('----分享----')
372 | self.share()
373 | self.coin_info()
374 | print(f'剩余金豆:{self.coin_count["totalCoin"]}')
375 | msgg.append(f'剩余金豆:{self.coin_count["totalCoin"]}')
376 | except Exception as e:
377 | print(f'手机号码:{self.phone.replace(self.phone[3:7], "****")},运行异常:{e}')
378 | msgg.append(f'手机号码:{self.phone.replace(self.phone[3:7], "****")},运行异常:{e}')
379 | finally:
380 | print(f'-----------')
381 | msgg.append(f'-----------')
382 |
383 |
384 | if __name__ == "__main__":
385 | try:
386 | result = checkCount()
387 | print('\n'.join(msgg))
388 | if not result:
389 | for item in account_list:
390 | if len(item) == 11:
391 | ChinaTelecom(item).main()
392 | print(f'【{item}】签到完成')
393 | else:
394 | print(f'你这个什么鬼?【{item}】')
395 | msgg.append(f'你这个什么鬼?【{item}】')
396 | except Exception as e:
397 | print(f'未知异常:{e}')
398 | msgg.append(f'未知异常:{e}')
399 | finally:
400 | # 发送青龙通知
401 | if sendNotify and msgg:
402 | # if sendNotify and os.path.exists("./sendNotify.js") and msgg:
403 | # if not os.path.exists("./invoke_notify.js"):
404 | # invoke_notify='''
405 | # const notify = require('./sendNotify.js');
406 | # notify.sendNotify(`${process.argv[2]}`, `${process.argv[3]}`);
407 | # '''
408 | # with open("./invoke_notify.js",'w',encoding='utf-8') as f:
409 | # f.write(invoke_notify)
410 | # f.close()
411 | content = '\n'.join(msgg)
412 | # os.system(f"node invoke_notify.js '{title}' '{content}'")
413 | notify.send(title, content)
414 |
--------------------------------------------------------------------------------
/ChinaTelecom/check_in_by_yz.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | # -------------------------------
4 | # @Author : github@limoruirui https://github.com/limoruirui by院长修改
5 | # @Time : 2022/9/12 16:10
6 | # cron "1 9,12 * * *" script-path=xxx.py,tag=匹配cron用
7 | # const $ = new Env('电信签到');
8 | # -------------------------------
9 |
10 | """
11 | 1. 电信签到 支持多账号执行 不需要抓包 脚本仅供学习交流使用, 请在下载后24h内删除
12 | 2. cron说明 12点必须执行一次(用于兑换) 然后12点之外还需要执行一次(用于执行每日任务) 一天共两次 可直接使用默认cron
13 | 3. 环境变量说明:
14 | 变量名(必须): TELECOM_PHONE_PASSWORD 格式: 手机号&服务密码,1317xxx1322&123456
15 | 单个CK塞多个账号时,以#分隔开:手机号&服务密码#手机号&服务密码,1317xxx1322&123456#1317xxx1322&123456
16 | 选填 TELECOM_FOOD : 给宠物喂食次数 默认为0 不喂食 根据用户在网时长 每天可以喂食5-10次
17 | 4. 必须登录过 电信营业厅 app的账号才能正常运行
18 | update:
19 | 2022.10.25 参考大佬 github@QGCliveDavis https://github.com/QGCliveDavis 的 loginAuthCipherAsymmertric 参数解密 新增app登录获取token 完成星播客系列任务 感谢大佬
20 | 2022.11.11 增加分享任务
21 | """
22 | from datetime import date, datetime
23 | from random import shuffle, randint, choices
24 | from time import sleep, strftime
25 | from re import findall
26 | from requests import get, post
27 | from base64 import b64encode
28 | from Utils.aes_encrypt import AES_Ctypt
29 | from Utils.rsa_encrypt import RSA_Encrypt
30 | from Utils.tool import timestamp, get_environ, print_now
31 | from Utils.telecom_login_by_yz import TelecomLogin
32 | from string import ascii_letters, digits
33 | import threading
34 |
35 | try:
36 | import Utils.notify
37 | except Exception as err: # 异常捕捉
38 | print(f'{err}\n加载通知服务失败~\n')
39 |
40 |
41 | class ChinaTelecom:
42 | def __init__(self, account, pwd, checkin=True):
43 | self.phone = account
44 | self.ticket = ""
45 | self.token = ""
46 | if pwd != "" and checkin:
47 | userLoginInfo = TelecomLogin(account, pwd).main()
48 | self.ticket = userLoginInfo[0]
49 | self.token = userLoginInfo[1]
50 |
51 | def init(self):
52 | self.msg = ""
53 | self.ua = f"CtClient;9.6.1;Android;12;SM-G9860;{b64encode(self.phone[5:11].encode()).decode().strip('=+')}!#!{b64encode(self.phone[0:5].encode()).decode().strip('=+')}"
54 | self.headers = {
55 | "Host": "wapside.189.cn:9001",
56 | "Referer": "https://wapside.189.cn:9001/resources/dist/signInActivity.html",
57 | "User-Agent": self.ua
58 | }
59 | self.key = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+ugG5A8cZ3FqUKDwM57GM4io6\nJGcStivT8UdGt67PEOihLZTw3P7371+N47PrmsCpnTRzbTgcupKtUv8ImZalYk65\ndU8rjC/ridwhw9ffW2LBwvkEnDkkKKRi2liWIItDftJVBiWOh17o6gfbPoNrWORc\nAdcbpk2L+udld5kZNwIDAQAB\n-----END PUBLIC KEY-----"
60 |
61 | def req(self, url, method, data=None):
62 | re_try = 3
63 | while re_try > 0:
64 | try:
65 | if method == "GET":
66 | data = get(url, headers=self.headers).json()
67 | return data
68 | elif method.upper() == "POST":
69 | data = post(url, headers=self.headers, json=data).json()
70 | return data
71 | else:
72 | print_now("您当前使用的请求方式有误,请检查")
73 | break
74 | except:
75 | re_try -= 1
76 | sleep(5)
77 | continue
78 |
79 | # 长明文分段rsa加密
80 | def telecom_encrypt(self, text):
81 | if len(text) <= 32:
82 | return RSA_Encrypt(self.key).encrypt(text)
83 | else:
84 | encrypt_text = ""
85 | for i in range(int(len(text) / 32) + 1):
86 | split_text = text[(32 * i):(32 * (i + 1))]
87 | encrypt_text += RSA_Encrypt(self.key).encrypt(split_text)
88 | return encrypt_text
89 |
90 | @staticmethod
91 | def geneRandomToken():
92 | randomList = choices(ascii_letters + digits, k=129)
93 | token = f"V1.0{''.join(x for x in randomList)}"
94 | return token
95 |
96 | # 签到
97 | def chech_in(self):
98 | url = "https://wapside.189.cn:9001/jt-sign/api/home/sign"
99 | data = {
100 | "encode": AES_Ctypt("34d7cb0bcdf07523").encrypt(
101 | f'{{"phone":{self.phone},"date":{timestamp()},"signSource":"smlprgrm"}}')
102 | }
103 | print_now(self.req(url, "post", data))
104 |
105 | # 获取任务列表
106 | def get_task(self):
107 | url = "https://wapside.189.cn:9001/jt-sign/paradise/getTask"
108 | data = {
109 | "para": self.telecom_encrypt(f'{{"phone":{self.phone}}}')
110 | }
111 | msg = self.req(url, "post", data)
112 | # print_now(dumps(msg, indent=2, ensure_ascii=False))
113 | if msg["resoultCode"] == "0":
114 | self.task_list = msg["data"]
115 | else:
116 | print_now("获取任务列表失败")
117 | print_now(msg)
118 | return
119 |
120 | # 做每日任务
121 | def do_task(self):
122 | url = "https://wapside.189.cn:9001/jt-sign/paradise/polymerize"
123 | for task in self.task_list:
124 | if "翻牌抽好礼" in task["title"] or "查看我的订单" in task["title"] or "查看我的云盘" in task["title"]:
125 | print_now(f'{task["title"]}----{task["taskId"]}')
126 | decrept_para = f'{{"phone":"{self.phone}","jobId":"{task["taskId"]}"}}'
127 | data = {
128 | "para": self.telecom_encrypt(decrept_para)
129 | }
130 | data = self.req(url, "POST", data)
131 | if data["data"]["code"] == 0:
132 | # print(data["resoultMsg"])
133 | print_now(data)
134 | else:
135 | print_now(f'聚合任务完成失败,原因是{data["resoultMsg"]}')
136 |
137 | # 给宠物喂食
138 | def food(self):
139 | url = "https://wapside.189.cn:9001/jt-sign/paradise/food"
140 | data = {
141 | "para": self.telecom_encrypt(f'{{"phone":{self.phone}}}')
142 | }
143 | res_data = self.req(url, "POST", data)
144 | if res_data["resoultCode"] == "0":
145 | print_now(res_data["resoultMsg"])
146 | else:
147 | print_now(f'聚合任务完成失败,原因是{res_data["resoultMsg"]}')
148 |
149 | # 查询宠物等级
150 | def get_level(self):
151 | url = "https://wapside.189.cn:9001/jt-sign/paradise/getParadiseInfo"
152 | body = {
153 | "para": self.telecom_encrypt(f'{{"phone":{self.phone}}}')
154 | }
155 | data = self.req(url, "POST", body)
156 | self.level = int(data["userInfo"]["paradiseDressup"]["level"])
157 | if self.level < 5:
158 | print_now("当前等级小于5级 不领取等级权益")
159 | return
160 | url = "https://wapside.189.cn:9001/jt-sign/paradise/getLevelRightsList"
161 | right_list = self.req(url, "POST", body)[f"V{self.level}"]
162 | for data in right_list:
163 | # print(dumps(data, indent=2, ensure_ascii=0))
164 | if "00金豆" in data["righstName"] or "话费" in data["righstName"]:
165 | rightsId = data["id"]
166 | self.level_ex(rightsId)
167 | continue
168 | # print(self.rightsId)
169 |
170 | # 每月领取等级金豆
171 | def level_ex(self, rightsId):
172 | # self.get_level()
173 | url = "https://wapside.189.cn:9001/jt-sign/paradise/conversionRights"
174 | data = {
175 | "para": self.telecom_encrypt(f'{{"phone":{self.phone},"rightsId":"{rightsId}"}},"receiveCount":1')
176 | }
177 | print_now(self.req(url, "POST", data))
178 |
179 | # 查询连续签到天数
180 | def query_signinfo(self):
181 | url = "https://wapside.189.cn:9001/jt-sign/reward/activityMsg"
182 | body = {
183 | "para": self.telecom_encrypt(f'{{"phone":{self.phone}}}')
184 | }
185 | data = self.req(url, "post", body)
186 | # print(dumps(data, indent=2, ensure_ascii=0))
187 | recordNum = data["recordNum"]
188 | if recordNum != 0:
189 | return data["date"]["id"]
190 | return ""
191 |
192 | # 若连续签到为7天 则兑换
193 | def convert_reward(self):
194 | url = "https://wapside.189.cn:9001/jt-sign/reward/convertReward"
195 | try:
196 | rewardId = self.query_signinfo() # "baadc927c6ed4d8a95e28fa3fc68cb9"
197 | except:
198 | rewardId = "baadc927c6ed4d8a95e28fa3fc68cb9"
199 | if rewardId == "":
200 | return
201 | body = {
202 | "para": self.telecom_encrypt(
203 | f'{{"phone":"{self.phone}","rewardId":"{rewardId}","month":"{date.today().__format__("%Y%m")}"}}')
204 | }
205 | for i in range(10):
206 | try:
207 | data = self.req(url, "post", body)
208 | except Exception as e:
209 | print(f"请求发送失败: " + str(e))
210 | sleep(6)
211 | continue
212 | print_now(data)
213 | if data["code"] == "0":
214 | break
215 | sleep(6)
216 | reward_status = self.get_coin_info()
217 | if reward_status:
218 | self.msg += f"账号{self.phone}连续签到7天兑换2元话费成功\n"
219 | print_now(self.msg)
220 | notify.send("电信签到兑换", self.msg)
221 | else:
222 | self.msg += f"账号{self.phone}连续签到7天兑换2元话费失败 明天会继续尝试兑换\n"
223 | print_now(self.msg)
224 | notify.send("电信签到兑换", self.msg)
225 |
226 | # 查询金豆数量
227 | def coin_info(self):
228 | url = "https://wapside.189.cn:9001/jt-sign/api/home/userCoinInfo"
229 | data = {
230 | "para": self.telecom_encrypt(f'{{"phone":{self.phone}}}')
231 | }
232 | self.coin_count = self.req(url, "post", data)
233 | print_now(self.coin_count)
234 |
235 | def author(self):
236 | """
237 | 通过usercode 获取 authorization
238 | :return:
239 | """
240 | self.get_usercode()
241 | url = "https://xbk.189.cn/xbkapi/api/auth/userinfo/codeToken"
242 | data = {
243 | "usercode": self.usercode
244 | }
245 | data = post(url, headers=self.headers_live, json=data).json()
246 | self.authorization = f"Bearer {data['data']['token']}"
247 | self.headers_live["Authorization"] = self.authorization
248 |
249 | def get_usercode(self):
250 | """
251 | 授权星播客登录获取 usercode
252 | :return:
253 | """
254 | url = f"https://xbk.189.cn/xbkapi/api/auth/dxzt?userID={self.ticket}&version=9.6.1&type=luckyDraw&active_code=20220425Qxtm5ycTcdMXdAR2LC"
255 | self.headers_live = {
256 | "User-Agent": self.ua,
257 | "Host": "xbk.189.cn",
258 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
259 | "Accept-Language": "zh-CN,zh-Hans;q=0.9"
260 | }
261 | location = get(url, headers=self.headers_live, allow_redirects=False).headers["location"]
262 | usercode = findall(r"usercode=(.*?)&", location)[0]
263 | self.usercode = usercode
264 |
265 | def watch_video(self):
266 | """
267 | 看视频 一天可完成6次
268 | :return:
269 | """
270 | url = "https://xbk.189.cn/xbkapi/lteration/liveTask/index/watchVideo"
271 | data = {
272 | "articleId": 3453
273 | }
274 | data = post(url, headers=self.headers_live, json=data).json()
275 | if data["code"] == 0:
276 | print("看小视频15s完成一次")
277 | else:
278 | print(f"完成看小视频15s任务失败, 失败原因为{data['msg']}")
279 |
280 | def like(self):
281 | """
282 | 点赞直播间 可完成5次
283 | :return:
284 | """
285 | url = "https://xbk.189.cn/xbkapi/lteration/room/like"
286 | liveId_list = [1820, 2032, 2466, 2565, 1094, 2422, 1858, 2346]
287 | shuffle(liveId_list)
288 | for liveId in liveId_list[:5]:
289 | data = {
290 | "account": self.phone,
291 | "liveId": liveId
292 | }
293 | try:
294 | data = post(url, headers=self.headers_live, json=data).json()
295 | if data["code"] == 8888:
296 | sleep(2)
297 | print(data["msg"])
298 | else:
299 | print(f"完成点赞直播间任务失败,原因是{data['msg']}")
300 | except Exception:
301 | print(Exception)
302 |
303 | def watch_live(self):
304 | # 首先初始化任务,等待15秒倒计时后再完成 可完成10次
305 | url = "https://xbk.189.cn/xbkapi/lteration/liveTask/index/watchLiveInit"
306 | live_id = randint(1000, 2700)
307 | data = {
308 | "period": 1,
309 | "liveId": live_id
310 | }
311 | data = post(url, headers=self.headers_live, json=data).json()
312 | if data["code"] == 0:
313 | taskcode = data["data"]
314 | url = "https://xbk.189.cn/xbkapi/lteration/liveTask/index/watchLive"
315 | data = {
316 | "key": taskcode,
317 | "period": 1,
318 | "liveId": live_id
319 | }
320 | print("正在等待15秒")
321 | sleep(15)
322 | data = post(url, headers=self.headers_live, json=data).json()
323 | if data["code"] == 0:
324 | print("完成1次观看直播任务")
325 | else:
326 | print(f"完成观看直播任务失败,原因是{data['msg']}")
327 | else:
328 | print(f"初始化观看直播任务失败,失败原因为{data['msg']}")
329 |
330 | def get_userid(self):
331 | url = "https://wapside.189.cn:9001/jt-sign/api/home/homeInfo"
332 | body = {
333 | "para": self.telecom_encrypt(
334 | f'{{"phone":"{self.phone}","signDate":"{datetime.now().__format__("%Y-%m")}"}}')
335 | }
336 | userid = post(url, json=body, headers=self.headers).json()["data"]["userInfo"]["userThirdId"]
337 | return userid
338 |
339 | def share(self):
340 | """
341 | 50的分享任务 token不做校检 有值即可 若登录成功了 使用自己的token 否则生成随机的token
342 | :return:
343 | """
344 | url = "https://appfuwu.189.cn:9021/query/sharingGetGold"
345 | body = {
346 | "headerInfos": {
347 | "code": "sharingGetGold",
348 | "timestamp": datetime.now().__format__("%Y%m%d%H%M%S"),
349 | "broadAccount": "",
350 | "broadToken": "",
351 | "clientType": "#9.6.1#channel50#iPhone 14 Pro Max#",
352 | "shopId": "20002",
353 | "source": "110003",
354 | "sourcePassword": "Sid98s",
355 | "token": self.token if self.token != "" else self.geneRandomToken(),
356 | "userLoginName": self.phone
357 | },
358 | "content": {
359 | "attach": "test",
360 | "fieldData": {
361 | "shareSource": "3",
362 | "userId": self.get_userid(),
363 | "account": TelecomLogin.get_phoneNum(self.phone)
364 | }
365 | }
366 | }
367 | headers = {
368 | "user-agent": "iPhone 14 Pro Max/9.6.1"
369 | }
370 | data = post(url, headers=headers, json=body).json()
371 | print_now(data)
372 |
373 | def main(self):
374 | self.init()
375 | self.chech_in()
376 | self.get_task()
377 | self.do_task()
378 | if foods != 0:
379 | for i in range(foods):
380 | self.food()
381 | # self.convert_reward()
382 | if datetime.now().day == 1:
383 | self.get_level()
384 | self.share()
385 | if self.ticket != "":
386 | self.author()
387 | for i in range(6):
388 | self.watch_video()
389 | sleep(15)
390 | # self.like()
391 | for i in range(10):
392 | try:
393 | self.watch_live()
394 | except:
395 | continue
396 | self.coin_info()
397 | self.msg += f"你账号{self.phone} 当前有金豆{self.coin_count['totalCoin']}"
398 | notify.send("电信app签到", self.msg)
399 |
400 | def get_coin_info(self):
401 | url = "https://wapside.189.cn:9001/jt-sign/api/getCoinInfo"
402 | decrept_para = f'{{"phone":"{self.phone}","pageNo":0,"pageSize":10,type:"1"}}'
403 | data = {
404 | "para": self.telecom_encrypt(decrept_para)
405 | }
406 | data = self.req(url, "POST", data)
407 | if "skuName" in data["data"]["biz"]["results"][0] and "连续签到" in data["data"]["biz"]["results"][0]["skuName"]:
408 | return True
409 | return False
410 |
411 |
412 | # 获取ck
413 | def get_cookie():
414 | ck_list = get_environ("TELECOM_PHONE_PASSWORD").split()
415 | cookie = None
416 | if len(ck_list) < 1:
417 | print('共配置{}条CK,请添加环境变量,或查看环境变量状态'.format(len(ck_list)))
418 | return ck_list
419 |
420 |
421 | def start(phone, password):
422 | if phone == "":
423 | exit(0)
424 | if password == "":
425 | print_now("电信服务密码未提供 只执行部分任务")
426 | if datetime.now().hour + (8 - int(strftime("%z")[2])) == 12:
427 | telecom = ChinaTelecom(phone, password, False)
428 | telecom.init()
429 | telecom.convert_reward()
430 | else:
431 | telecom = ChinaTelecom(phone, password)
432 | telecom.main()
433 | print("\n")
434 | print("\n")
435 |
436 |
437 | if __name__ == '__main__':
438 | l = []
439 | user_map = []
440 | cklist = get_cookie()
441 | for i in range(len(cklist)):
442 | # 以#分割开的ck
443 | split1 = cklist[i].split("#")
444 | if len(split1) > 1:
445 | for j in range(len(split1)):
446 | split2 = split1[j].split("&")
447 | if len(split2) > 1:
448 | user_map.append(split1[j])
449 | else:
450 | userinfo = cklist[i].split("&")
451 | if len(userinfo) > 1:
452 | user_map.append(cklist[i])
453 |
454 | foods = int(float(get_environ("TELECOM_FOOD", 0, False)))
455 | for i in range(len(user_map)):
456 | phone = ""
457 | password = ""
458 | userinfo = user_map[i].split("&")
459 | if len(userinfo) > 1:
460 | phone = userinfo[0]
461 | password = userinfo[1]
462 | print('开始执行第{}个账号:{}'.format((i + 1), phone))
463 | if phone == "":
464 | print("当前账号未填写手机号 跳过")
465 | print("\n")
466 | continue
467 | p = threading.Thread(target=start, args=(phone, password))
468 | l.append(p)
469 | p.start()
470 | print("\n")
471 | for i in l:
472 | i.join()
473 |
--------------------------------------------------------------------------------
/ChinaTelecom/live_by_yz.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | # -------------------------------
4 | # @Author : github@limoruirui https://github.com/limoruirui by院长修改
5 | # @Time : 2022/11/11 10:42
6 | # cron "*/30 8-23 * * *" script-path=xxx.py,tag=匹配cron用
7 | # const $ = new Env('某营业厅直播抽奖');
8 | # -------------------------------
9 | """
10 | 1. 脚本仅供学习交流使用, 请在下载后24h内删除
11 | 2. 星播客手动抽奖,只添加直播时间,具体查看日志和消息推送
12 | """
13 | from re import findall
14 | from random import randint
15 | from base64 import b64encode
16 | from time import mktime, strptime, strftime, sleep as time_sleep
17 | from requests import post, get, packages
18 | from datetime import datetime, timedelta
19 | from asyncio import wait, sleep, run
20 | from Utils.tool import timestamp, print_now
21 | import Utils.notify as notify
22 | from check_in_by_yz import ChinaTelecom
23 | import time
24 | import requests
25 |
26 | packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ":HIGH:!DH:!aNULL"
27 |
28 |
29 | class TelecomLotter:
30 | def __init__(self, phone, password):
31 | self.phone = phone
32 | chinaTelecom = ChinaTelecom(phone, password)
33 | chinaTelecom.init()
34 | chinaTelecom.author()
35 | self.authorization = chinaTelecom.authorization
36 | self.ua = chinaTelecom.ua
37 | self.token = chinaTelecom.token
38 |
39 | def get_action_id(self, liveId):
40 | url = "https://appkefu.189.cn:8301/query/getWaresList"
41 | body = {
42 | "headerInfos": {
43 | "code": "getWaresList",
44 | "timestamp": datetime.now().__format__("%Y%m%d%H%M%S"),
45 | "broadAccount": "",
46 | "broadToken": "",
47 | "clientType": "#9.6.1#channel128#samsung SM-G9860#",
48 | "shopId": "20002",
49 | "source": "110003",
50 | "sourcePassword": "Sid98s",
51 | "token": self.token,
52 | "userLoginName": self.phone
53 | },
54 | "content": {
55 | "attach": "test",
56 | "fieldData": {
57 | "limit": "",
58 | "page": "1",
59 | "liveId": liveId
60 | }
61 | }
62 | }
63 | headers = {
64 | "User-Agent": self.ua,
65 | "authorization": self.authorization
66 | }
67 | data = post(url, headers=headers, json=body).json()
68 | try:
69 | for waresInfo in data["responseData"]["data"]["waresInfos"]:
70 | print(waresInfo["title"])
71 | if "转盘" in waresInfo["title"] or "抽奖" in waresInfo["title"]:
72 | active_code = findall(r"active_code\u003d(.*?)\u0026", waresInfo["link"])[0]
73 | return active_code
74 | return None
75 | except:
76 | return None
77 |
78 | def get_action_id_other(self, liveId):
79 | def encrypt_phone():
80 | result = ""
81 | for i in self.phone:
82 | result += chr(ord(i) + 2)
83 | return result
84 |
85 | url = "https://wapmkt.189.cn:8301/query/directSeedingInfo"
86 | body = {
87 | "headerInfos": {
88 | "code": "directSeedingInfo",
89 | "timestamp": datetime.now().__format__("%Y%m%d%H%M%S"),
90 | "broadAccount": "",
91 | "broadToken": "",
92 | "clientType": "#9.6.1#channel128#samsung SM-G9860#",
93 | "shopId": "20002",
94 | "source": "110003",
95 | "sourcePassword": "Sid98s",
96 | "token": self.token,
97 | "userLoginName": self.phone
98 | },
99 | "content": {
100 | "attach": "test",
101 | "fieldData": {
102 | "liveId": liveId,
103 | "account": encrypt_phone()
104 | }
105 | }
106 | }
107 | headers = {
108 | "User-Agent": self.ua,
109 | "authorization": self.authorization
110 | }
111 | data = post(url, headers=headers, json=body).json()["responseData"]["data"]
112 | try:
113 | if data["buoyLink"] is None:
114 | return None
115 | active_code = findall(r"active_code\u003d(.*?)\u0026", data["buoyLink"])[0]
116 | return active_code
117 | except:
118 | return None
119 |
120 | def lotter(self, liveId, period):
121 | """
122 | :param liveId: 直播间id
123 | :param period: 某个参数 暂不明意义 查询直播间信息时会返回
124 | :return:
125 | """
126 | print_now(f"当前执行的直播间id为{liveId}")
127 | for i in range(2):
128 | # active_code1 查询直播间购物车中的大转盘活动id
129 | active_code1 = self.get_action_id(liveId)
130 | # active_code2 查询直播间非购物车 而是右上角的大转盘活动id
131 | active_code2 = self.get_action_id_other(liveId)
132 | if active_code1 is not None or active_code2 is not None:
133 | break
134 | print(f"此直播间暂无抽奖活动, 等待10秒后再次查询 剩余查询次数{2 - i}")
135 | continue
136 | if active_code1 is None and active_code2 is None:
137 | print("查询结束 本直播间暂无抽奖活动")
138 | return
139 | elif active_code1 is None or active_code2 is None:
140 | active_code = active_code1 if active_code2 is None else active_code2
141 | active_code_list = [active_code]
142 | else:
143 | active_code_list = [active_code1, active_code2]
144 | for active_code in active_code_list:
145 | url = "https://xbk.189.cn/xbkapi/active/v2/lottery/do"
146 | body = {
147 | "active_code": active_code,
148 | "liveId": liveId,
149 | "period": period
150 | }
151 | headers = {
152 | "User-Agent": self.ua,
153 | "authorization": self.authorization
154 | }
155 | data = post(url, headers=headers, json=body).json()
156 | print(data)
157 | if data["code"] == 0:
158 | notify.send("直播抽奖", f"{self.phone}: 获得了{data['data']['title']}")
159 |
160 | def find_price(self):
161 | url = "https://xbk.189.cn/xbkapi/active/v2/lottery/getMyWinList?page=1&give_status=200&activeCode="
162 | headers = {
163 | "User-Agent": self.ua,
164 | "authorization": self.authorization
165 | }
166 | data = get(url, headers=headers).json()
167 | if data["code"] == 0:
168 | all_price_list = data["data"]
169 | compare_date = lambda date: date.split("-")[1] == str(
170 | (datetime.now() + timedelta(hours=8 - int(strftime("%z")[2]))).month)
171 | month_price = [f'{info["win_time"]}: {info["title"]}' for info in all_price_list if
172 | compare_date(info["win_time"])]
173 | month_price_info = "\n".join(month_price)
174 | print(month_price_info)
175 | notify.send("本月直播奖品查询", f"{self.phone}:\n{month_price_info}")
176 | else:
177 | print(f"获取奖品信息失败, 接口返回" + str(data))
178 |
179 |
180 | def get_data():
181 | print('正在加载今日直播数据ing...')
182 | all_list = []
183 | code = 1
184 | msg_str = ""
185 | for i in range(35):
186 | if code < 10:
187 | code_str = '0' + str(code)
188 | else:
189 | code_str = str(code)
190 | url = f'https://xbk.189.cn/xbkapi/lteration/index/recommend/anchorRecommend?provinceCode={code_str}'
191 | random_phone = f"1537266{randint(1000, 9999)}"
192 | headers = {
193 | "referer": "https://xbk.189.cn/xbk/newHome?version=9.4.0&yjz=no&l=card&longitude=%24longitude%24&latitude=%24latitude%24&utm_ch=hg_app&utm_sch=hg_sh_shdbcdl&utm_as=xbk_tj&loginType=1",
194 | "user-agent": f"CtClient;9.6.1;Android;12;SM-G9860;{b64encode(random_phone[5:11].encode()).decode().strip('=+')}!#!{b64encode(random_phone[0:5].encode()).decode().strip('=+')}"
195 | }
196 | # print(url)
197 | data = requests.get(url, headers=headers).json()
198 | body = data["data"]
199 | for i in body:
200 | if time.strftime('%Y-%m-%d') in i['start_time']:
201 | if i not in all_list:
202 | # print('今日开播时间:'+i['start_time']+' 直播间名称:'+i['nickname'] )
203 | # print('安卓浏览器直接打开链接 ctclient://startapp/android/open?LinkType=5&Link=https://xbk.189.cn/xbk/livingRoom?liveId='+str(i['liveId']) )
204 | # print('直接打开链接 https://xbk.189.cn/xbk/livingRoom?liveId='+str(i['liveId']) )
205 | # print('\n')
206 | msg_str += '今日开播时间:' + i['start_time'] + ' 直播间名称:' + i[
207 | 'nickname'] + '\n安卓浏览器直接打开链接:\nctclient://startapp/android/open?LinkType=5&Link=https://xbk.189.cn/xbk/livingRoom?liveId=' + str(
208 | i['liveId']) + '\n直接打开链接:\nhttps://xbk.189.cn/xbk/livingRoom?liveId=' + str(
209 | i['liveId']) + '\n\n'
210 | all_list.append(i)
211 | code += 1
212 | list = {}
213 | f = 1
214 | for i in all_list:
215 | list['liveRoom' + str(f)] = i
216 | f += 1
217 | print('直播数据加载完毕')
218 | print('\n')
219 | # 发送消息
220 | notify.send('电信星播客直播通知', msg_str)
221 | return list
222 |
223 |
224 | def main(phone, password):
225 | apiType = 1
226 | # 切换使用直接加载方式
227 | data = getData
228 | # try:
229 | # url = "https://gitee.com/kele2233/genxin/raw/master/telecomLiveInfo.json"
230 | # data = get(url, timeout=5).json()
231 | # except:
232 | # print("主直播接口失效,进入备用抓包接口")
233 | # data = list_d
234 | # # try:
235 | # # url = "https://raw.githubusercontent.com/limoruirui/Hello-Wolrd/main/telecomLiveInfo.json"
236 | # # #url = "https://api.ruirui.fun/telecom/getLiveInfo"
237 | # # data = get(url, timeout=5).json()
238 | # # except:
239 | # # url = "https://xbk.189.cn/xbkapi/lteration/index/recommend/anchorRecommend?provinceCode=01"
240 | # # random_phone = f"1537266{randint(1000, 9999)}"
241 | # # headers = {
242 | # # "referer": "https://xbk.189.cn/xbk/newHome?version=9.4.0&yjz=no&l=card&longitude=%24longitude%24&latitude=%24latitude%24&utm_ch=hg_app&utm_sch=hg_sh_shdbcdl&utm_as=xbk_tj&loginType=1",
243 | # # "user-agent": f"CtClient;9.6.1;Android;12;SM-G9860;{b64encode(random_phone[5:11].encode()).decode().strip('=+')}!#!{b64encode(random_phone[0:5].encode()).decode().strip('=+')}"
244 | # # }
245 | # # data = get(url, headers=headers).json()
246 | # # apiType = 2
247 | print(data)
248 | liveListInfo = {}
249 | allLiveInfo = data.values() if apiType == 1 else data["data"]
250 | for liveInfo in allLiveInfo:
251 | if 1740 > timestamp(True) - int(mktime(strptime(liveInfo["start_time"], "%Y-%m-%d %H:%M:%S"))) + (
252 | 8 - int(strftime("%z")[2])) * 3600 > 0:
253 | liveListInfo[liveInfo["liveId"]] = liveInfo["period"]
254 | if len(liveListInfo) == 0:
255 | print("查询结束 没有近期开播的直播间")
256 | else:
257 | telecomLotter = TelecomLotter(phone, password)
258 | all_task = [telecomLotter.lotter(liveId, period) for liveId, period in liveListInfo.items()]
259 | run(wait(all_task))
260 | now = datetime.now()
261 | if now.hour == 12 + int(strftime("%z")[2]) and now.minute > 10:
262 | TelecomLotter(phone, password).find_price()
263 |
264 |
265 | def start(phone, password):
266 | if phone == "" or password == "":
267 | print("未填写相应变量 退出")
268 | exit(0)
269 | main(phone, password)
270 | print("\n")
271 |
272 |
273 | if __name__ == '__main__':
274 | getData = []
275 | # try:
276 | # url = "https://gitcode.net/weixin_52142858/telecomliveinfo/-/raw/master/telecomLiveInfo.json"
277 | # getData = get(url, timeout=5).json()
278 | # except:
279 | # #加载今日直播信息
280 | # print('主接口失效,使用备用接口中。。。。')
281 | # getData=get_data()
282 |
283 | l = []
284 | getData = get_data()
285 | # user_map = []
286 | # cklist = get_cookie("TELECOM_PHONE_PASSWORD")
287 | # for i in range(len(cklist)):
288 | # #以#分割开的ck
289 | # split1 = cklist[i].split("#")
290 | # if len(split1)>1:
291 | # for j in range(len(split1)):
292 | # split2 = split1[j].split("&")
293 | # if len(split2)>1:
294 | # user_map.append(split1[j])
295 | # else:
296 | # userinfo = cklist[i].split("&")
297 | # if len(userinfo)>1:
298 | # user_map.append(cklist[i])
299 |
300 | # for i in range(len(user_map)):
301 | # phone=""
302 | # password=""
303 | # userinfo = user_map[i].split("&")
304 | # if len(userinfo)>1:
305 | # phone = userinfo[0]
306 | # password = userinfo[1]
307 | # print('开始执行第{}个账号:{}'.format((i+1),phone))
308 | # if phone == "" or password == "":
309 | # print("当前账号未填写手机号或者密码 跳过")
310 | # print("\n")
311 | # continue
312 | # p = threading.Thread(target=start,args=(phone,password))
313 | # l.append(p)
314 | # p.start()
315 | # print("\n")
316 | # for i in l:
317 | # i.join()
318 |
--------------------------------------------------------------------------------
/ChinaUnicom/read.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | """
4 | -------------------------------
5 | @Author : github@limoruirui https://github.com/yuanter/misaka/blob/master/china_unicom.py
6 | @Time : 2022/8/10 13:23
7 | -------------------------------
8 | cron "30 9 * * *" script-path=xxx.py,tag=匹配cron用
9 | const $ = new Env('某通阅读');
10 | 联通app抽奖 入口:联通app 搜索 阅读专区 进入话费派送中
11 | 1. 脚本仅供学习交流使用, 请在下载后24h内删除
12 | 2. 需要第三方库 pycryptodome 支持 命令行安装 pip3 install pycryptodome或者根据自己环境自行安装
13 | 3. 环境变量说明 PHONE_NUM(必需) UNICOM_LOTTER(选填) 自行新建环境变量添加
14 | PHONE_NUM 格式:手机号#UA 多账号使用&隔开 UA(选填) 为联通app的useragent 随便一个数据包的请求头里应该都有 建议自己抓一个填上 不填也能跑 数据内容参考 line 49 双引号的内容
15 | UA例子:Mozilla/5.0 (iPhone; CPU iPhone OS 15_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@10.0001}
16 | UNICOM_LOTTER 默认自动抽奖, 若不需要 则添加环境变量值为 False
17 | updateTime: 2023.1.1 log: 更新aes加密key
18 | updateTime: 2022.12.1 log: 活动重新上架 改用 pycryptodome 替代 cryptography 进行aes加密
19 | updateTime: 2022.9.1 log: 每个月活动id改变更新
20 | """
21 | from requests import post, get
22 | from time import sleep, time
23 | from datetime import datetime
24 | from hashlib import md5 as md5Encode
25 | from random import randint, uniform, choice
26 | from sys import stdout, exit
27 | from base64 import b64encode
28 | from json import dumps
29 | from Utils.encrypt_symmetric import Crypt
30 | from Utils.tool import get_environ
31 | import threading
32 |
33 | try:
34 | from Utils.notify import send
35 | except Exception as err: # 异常捕捉
36 | print(f'{err}\n加载通知服务失败~\n')
37 |
38 | msg_str = ""
39 |
40 | """主类"""
41 |
42 |
43 | class China_Unicom:
44 | def __init__(self, phone_num, run_ua):
45 | self.phone_num = phone_num
46 | default_ua = f"Mozilla/5.0 (Linux; Android {randint(8, 13)}; SM-S908U Build/TP1A.220810.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/{randint(95, 108)}.0.5359.128 Mobile Safari/537.36; unicom{{version:android@9.0{randint(0,6)}00,desmobile:{self.phone_num}}};devicetype{{deviceBrand:,deviceModel:}};{{yw_code:}}"
47 | #run_ua = get_environ(key="UNICOM_USERAGENT", default=default_ua, output=False)
48 | if run_ua is None or run_ua == "":
49 | run_ua = default_ua
50 | # print("使用的UA:"+run_ua)
51 |
52 | self.headers = {
53 | "Host": "10010.woread.com.cn",
54 | "Accept": "application/json, text/plain, */*",
55 | "Accept-Language": "zh-CN,zh-Hans;q=0.9",
56 | "Accept-Encoding": "gzip, deflate, br",
57 | "Content-Type": "application/json;charset=utf-8",
58 | "Origin": "https://10010.woread.com.cn",
59 | "User-Agent": run_ua,
60 | "Connection": "keep-alive",
61 | "Referer": "https://10010.woread.com.cn/ng_woread/",
62 | }
63 | self.fail_num = 0
64 |
65 | def timestamp(self):
66 | return round(time() * 1000)
67 |
68 | def print_now(self, content):
69 | print(content)
70 | stdout.flush()
71 |
72 | def md5(self, str):
73 | m = md5Encode(str.encode(encoding='utf-8'))
74 | return m.hexdigest()
75 |
76 | def req(self, url, crypt_text, retry_num=5):
77 | while retry_num > 0:
78 | body = {
79 | "sign": b64encode(Crypt(crypt_type="AES", key="update!@#1234567", iv="16-Bytes--String", mode="CBC").encrypt(crypt_text).encode()).decode()
80 | }
81 | self.headers["Content-Length"] = str(len(dumps(body).replace(" ", "")))
82 | try:
83 | res = post(url, headers=self.headers, json=body)
84 | data = res.json()
85 | return data
86 | except Exception as e:
87 | print(f"本次请求失败, 正在重新发送请求 剩余次数{retry_num}")
88 | print(f"本次请求失败原因------{e}")
89 | retry_num -= 1
90 | sleep(5)
91 | return self.req(url, crypt_text, retry_num)
92 |
93 | def referer_login(self):
94 | date = datetime.today().__format__("%Y%m%d%H%M%S")
95 | timestamp = self.timestamp()
96 | url = f"https://10010.woread.com.cn/ng_woread_service/rest/app/auth/10000002/{timestamp}/{self.md5(f'100000027k1HcDL8RKvc{timestamp}')}"
97 | crypt_text = f'{{"timestamp":"{date}"}}'
98 | body = {
99 | "sign": b64encode(Crypt(crypt_type="AES", key="1234567890abcdef").encrypt(crypt_text).encode()).decode()
100 | }
101 | self.headers["Content-Length"] = str(len(str(body)) - 1)
102 | data = post(url, headers=self.headers, json=body).json()
103 | if data["code"] == "0000":
104 | self.headers["accesstoken"] = data["data"]["accesstoken"]
105 | else:
106 | self.print_now(f"设备登录失败,日志为{data}")
107 | exit(0)
108 |
109 | def get_userinfo(self):
110 | date = datetime.today().__format__("%Y%m%d%H%M%S")
111 | url = "https://10010.woread.com.cn/ng_woread_service/rest/account/login"
112 | crypt_text = f'{{"phone":"{self.phone_num}","timestamp":"{date}"}}'
113 | data = self.req(url, crypt_text)
114 | if data["code"] == "0000":
115 | self.userinfo = data["data"]
116 | else:
117 | self.print_now(f"手机号登录失败, 日志为{data}")
118 | exit(0)
119 |
120 | def watch_video(self):
121 | global msg_str #声明我们在函数内部使用的是在函数外部定义的全局变量msg_str
122 | self.print_now("看广告获取积分任务: ")
123 | url = "https://10010.woread.com.cn/ng_woread_service/rest/activity/yearEnd/obtainScoreByAd"
124 | date = datetime.today().__format__("%Y%m%d%H%M%S")
125 | crypt_text = f'{{"value":"947728124","timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
126 | for i in range(3):
127 | data = self.req(url, crypt_text)
128 | self.print_now(data)
129 | if self.fail_num == 3:
130 | self.print_now("当前任务出现异常 且错误次数达到3次 请手动检查")
131 | # push("某通阅读", "当前任务出现异常 且错误次数达到3次 请手动检查")
132 | msg_str += f"账号{self.phone_num}当前任务出现异常 且错误次数达到3次 请手动检查\n\n"
133 | exit(0)
134 | if data["code"] == "9999":
135 | self.print_now("当前任务出现异常 正在重新执行")
136 | self.fail_num += 1
137 | self.main()
138 | return False
139 | sleep(uniform(2, 8))
140 | return True
141 |
142 | def read_novel(self):
143 | global msg_str #声明我们在函数内部使用的是在函数外部定义的全局变量msg_str
144 | self.get_cardid()
145 | self.get_cntindex()
146 | self.get_chapterallindex()
147 | self.print_now(f"你的账号{self.phone_num} :正在执行观看300次小说, 此过程较久, 最大时长为300 * 8s = 40min")
148 | for i in range(300):
149 | date = datetime.today().__format__("%Y%m%d%H%M%S")
150 | chapterAllIndex = choice(self.chapterallindex_list)
151 | url = f"https://10010.woread.com.cn/ng_woread_service/rest/cnt/wordsDetail?catid={self.catid}&pageIndex={self.pageIndex}&cardid={randint(10000, 99999)}&cntindex={self.cntindex}&chapterallindex={chapterAllIndex}&chapterseno=3"
152 | crypt_text = f'{{"chapterAllIndex":{chapterAllIndex},"cntIndex":{self.cntindex},"cntTypeFlag":"1","timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
153 | data = self.req(url, crypt_text)
154 | if self.fail_num == 3:
155 | self.print_now("当前任务出现异常 且错误次数达到3次 请手动检查")
156 | # push("某通阅读", "阅读任务出现异常 且错误次数达到3次 请手动检查")
157 | msg_str += f"账号{self.phone_num}阅读任务出现异常 且错误次数达到3次 请手动检查\n\n"
158 | exit(0)
159 | if data.get("code") != "0000":
160 | self.print_now("阅读小说发生异常, 正在重新登录执行, 接口返回")
161 | self.print_now(data)
162 | return self.main()
163 | sleep(uniform(2, 4))
164 |
165 | def query_score(self):
166 | url = "https://10010.woread.com.cn/ng_woread_service/rest/activity/yearEnd/queryUserScore"
167 | date = datetime.today().__format__("%Y%m%d%H%M%S")
168 | crypt_text = f'{{"activeIndex":{self.activeIndex},"timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
169 | data = self.req(url, crypt_text)
170 | total_score = data["data"]["validScore"]
171 | self.lotter_num = int(total_score / 50)
172 | self.print_now(f"你的账号{self.phone_num}当前有积分{total_score}, 可以抽奖{self.lotter_num}次")
173 |
174 | def get_activetion_id(self):
175 | url = "https://10010.woread.com.cn/ng_woread_service/rest/activity/yearEnd/queryActiveInfo"
176 | date = datetime.today().__format__("%Y%m%d%H%M%S")
177 | crypt_text = f'{{"timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
178 | data = self.req(url, crypt_text)
179 | if data["code"] == "0000":
180 | self.activeIndex = data["data"]["activeindex"]
181 | else:
182 | self.print_now(f"活动id获取失败 将影响抽奖和查询积分")
183 | def get_cardid(self):
184 | """
185 | 获取cardid
186 | :return:
187 | """
188 | url = "https://10010.woread.com.cn/ng_woread_service/rest/basics/getIntellectRecommend"
189 | date = datetime.today().__format__("%Y%m%d%H%M%S")
190 | crypt_text = f'{{"cntsize":8,"recommendsize":5,"recommendid":0,"timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
191 | data = self.req(url, crypt_text)
192 | # print(data)
193 | self.pageIndex = data["data"]["recommendindex"] if "recommendindex" in data["data"] else "10725"
194 | self.cardid = data["data"]["catindex"] if "catindex" in data["data"] else "119056"
195 | def get_cntindex(self):
196 | url = "https://10010.woread.com.cn/ng_woread_service/rest/basics/recommposdetail/12279"
197 | self.headers.pop("Content-Length", "no")
198 | data = get(url, headers=self.headers).json()
199 | booklist = data["data"]["booklist"]["message"]
200 | book_num = len(booklist)
201 | self.catid = booklist[0]["catindex"] if "catindex" in booklist[0] else "119411"
202 | self.cntindex = booklist[randint(0, book_num - 1)]["cntindex"]
203 | def get_chapterallindex(self):
204 | url = f"https://10010.woread.com.cn/ng_woread_service/rest/cnt/chalist?catid=119411&pageIndex=10725&cardid=12279&cntindex={self.cntindex}"
205 | date = datetime.today().__format__("%Y%m%d%H%M%S")
206 | crypt_text = f'{{"curPage":1,"limit":30,"index":"{self.cntindex}","sort":0,"finishFlag":1,"timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
207 | data = self.req(url, crypt_text)
208 | chapterallindexlist = data["list"][0]["charptercontent"]
209 | chapterallindex_num = len(chapterallindexlist)
210 | self.chapterallindex_list = [0] * chapterallindex_num
211 | i = 0
212 | while i < chapterallindex_num:
213 | self.chapterallindex_list[i] = chapterallindexlist[i]["chapterallindex"]
214 | i += 1
215 | def lotter(self):
216 | url = "https://10010.woread.com.cn/ng_woread_service/rest/activity/yearEnd/handleDrawLottery"
217 | date = datetime.today().__format__("%Y%m%d%H%M%S")
218 | crypt_text = f'{{"activeIndex":{self.activeIndex},"timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
219 | data = self.req(url, crypt_text)
220 | if data["code"] == "0000":
221 | self.print_now(f"抽奖成功, 获得{data['data']['prizename']}")
222 | else:
223 | self.print_now(f"抽奖失败, 日志为{data}")
224 |
225 | def watch_ad(self):
226 | self.print_now("观看广告得话费红包: ")
227 | url = "https://10010.woread.com.cn/ng_woread_service/rest/activity/userTakeActive"
228 | date = datetime.today().__format__("%Y%m%d%H%M%S")
229 | crypt_text = f'{{"activeIndex":6880,"timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
230 | data = self.req(url, crypt_text)
231 | self.print_now(data)
232 |
233 | def exchange(self):
234 | # ticketValue activeid来自于https://10010.woread.com.cn/ng_woread_service/rest/phone/vouchers/getSysConfig get请求
235 | # {"ticketValue":"300","activeid":"61yd210901","timestamp":"20220816213709","token":"","userId":"","userIndex":,"userAccount":"","verifyCode":""}
236 | url = "https://10010.woread.com.cn/ng_woread_service/rest/phone/vouchers/exchange"
237 | date = datetime.today().__format__("%Y%m%d%H%M%S")
238 | crypt_text = f'{{"ticketValue":"300","activeid":"61yd210901","timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
239 | data = self.req(url, crypt_text)
240 | print(data)
241 |
242 | def query_red(self):
243 | global msg_str #声明我们在函数内部使用的是在函数外部定义的全局变量msg_str
244 | url = "https://10010.woread.com.cn/ng_woread_service/rest/phone/vouchers/queryTicketAccount"
245 | date = datetime.today().__format__("%Y%m%d%H%M%S")
246 | crypt_text = f'{{"timestamp":"{date}","token":"{self.userinfo["token"]}","userId":"{self.userinfo["userid"]}","userIndex":{self.userinfo["userindex"]},"userAccount":"{self.userinfo["phone"]}","verifyCode":"{self.userinfo["verifycode"]}"}}'
247 | data = self.req(url, crypt_text)
248 | if data["code"] == "0000":
249 | can_use_red = data["data"]["usableNum"] / 100
250 | if can_use_red >= 3:
251 | self.print_now(f"账号{self.phone_num}查询成功 你当前有话费红包{can_use_red} 可以去兑换了")
252 | # push("某通阅读", f"账号{self.phone_num}查询成功 你当前有话费红包{can_use_red} 可以去兑换了")
253 | msg_str += f"账号{self.phone_num}查询成功 你当前有话费红包{can_use_red} 可以去兑换了\n\n"
254 | else:
255 | self.print_now(f"账号{self.phone_num}查询成功 你当前有话费红包{can_use_red} 不足兑换的最低额度")
256 | # push("某通阅读", f"账号{self.phone_num}查询成功 你当前有话费红包{can_use_red} 不足兑换的最低额度")
257 | msg_str += f"账号{self.phone_num}查询成功 你当前有话费红包{can_use_red} 不足兑换的最低额度\n\n"
258 |
259 | def main(self):
260 | self.referer_login()
261 | self.get_userinfo()
262 | if not self.watch_video():
263 | return
264 | self.get_activetion_id()
265 | self.read_novel()
266 | self.query_score()
267 | self.watch_ad()
268 | if unicom_lotter:
269 | for i in range(self.lotter_num):
270 | self.lotter()
271 | sleep(2)
272 | self.query_score()
273 | self.query_red()
274 |
275 |
276 | def start(phone, run_ua):
277 | if phone == "":
278 | exit(0)
279 | China_Unicom(phone, run_ua).main()
280 | print("\n")
281 | print("\n")
282 |
283 |
284 | if __name__ == "__main__":
285 | unicom_lotter = get_environ("UNICOM_LOTTER", True, False)
286 | """读取环境变量"""
287 | l = []
288 | user_map = get_environ("PHONE_NUM").split(",")
289 |
290 | for i in range(len(user_map)):
291 | phone = ""
292 | info = user_map[i].split("&")[0]
293 | # 以#分割开的ck
294 | split1 = info.split("#")
295 | run_ua = ""
296 | phone = split1[0]
297 | if len(split1) > 1:
298 | run_ua = split1[1] + f";devicetype{{deviceBrand:,deviceModel:}};{{yw_code:}}"
299 |
300 | print('开始执行第{}个账号:{}'.format((i + 1), phone))
301 | if phone == "":
302 | print("当前账号未填写手机号 跳过")
303 | print("\n")
304 | continue
305 | p = threading.Thread(target=start, args=(phone, run_ua))
306 | l.append(p)
307 | p.start()
308 | print("\n")
309 | for i in l:
310 | i.join()
311 | send("联通阅读", msg_str)
312 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/MIUI/check_in.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import time
3 | import json
4 | import hashlib
5 |
6 | from urllib import request
7 | from http import cookiejar
8 |
9 | from utils import system_info, get_config, w_log, s_log, check_config, format_config, random_sleep, sleep_ten_sec_more
10 |
11 |
12 | class MIUITask:
13 |
14 | def __init__(self, uid, password, user_agent, device_id):
15 | self.uid = uid
16 | self.password = password
17 | self.user_agent = user_agent
18 | self.device_id = device_id
19 |
20 | # 留空
21 | self.cookie = ''
22 | # 留空
23 | self.miui_vip_ph = ''
24 |
25 | # 签名
26 | def post_sign(self, data):
27 | s_data = []
28 | for d in data:
29 | s_data.append(str(d) + '=' + str(data[d]))
30 | s_str = '&'.join(s_data)
31 | w_log('签名原文:' + str(s_str))
32 | s_str = hashlib.md5(str(s_str).encode(encoding='UTF-8')).hexdigest() + '067f0q5wds4'
33 | s_sign = hashlib.md5(str(s_str).encode(encoding='UTF-8')).hexdigest()
34 | w_log('签名结果:' + str(s_sign))
35 | return s_sign, data['timestamp']
36 |
37 | # 点赞
38 | def thumb_up(self):
39 | headers = {
40 | 'cookie': str(self.cookie)
41 | }
42 | sign_data = {
43 | 'postId': '36625780',
44 | 'timestamp': int(round(time.time() * 1000))
45 | }
46 | sign = self.post_sign(sign_data)
47 | data = {
48 | 'postId': '36625780',
49 | 'sign': sign[0],
50 | 'timestamp': sign[1]
51 | }
52 | try:
53 | response = requests.get('https://api.vip.miui.com/mtop/planet/vip/content/announceThumbUp', headers=headers,
54 | params=data)
55 | r_json = response.json()
56 | if r_json['code'] == 401:
57 | return w_log("点赞失败:Cookie无效")
58 | elif r_json['code'] != 200:
59 | return w_log("点赞失败:" + str(r_json['message']))
60 | w_log("点赞成功")
61 | except Exception as e:
62 | w_log("点赞出错")
63 | w_log(e)
64 |
65 | # 取消点赞
66 | def cancel_thumb_up(self):
67 | headers = {
68 | 'cookie': str(self.cookie)
69 | }
70 | try:
71 | response = requests.get('https://api.vip.miui.com/api/community/post/cancelThumbUp?postId=36625780',
72 | headers=headers)
73 | r_json = response.json()
74 | if r_json['code'] == 401:
75 | return w_log("取消点赞失败:Cookie无效")
76 | elif r_json['code'] != 200:
77 | return w_log("取消点赞失败:" + str(r_json['message']))
78 | w_log("取消点赞成功")
79 | except Exception as e:
80 | w_log("取消点赞出错")
81 | w_log(e)
82 |
83 | def get_vip_cookie(self, url):
84 |
85 | try:
86 | r_cookie = cookiejar.CookieJar()
87 | handler = request.HTTPCookieProcessor(r_cookie)
88 | opener = request.build_opener(handler)
89 | response = opener.open(url)
90 | for item in r_cookie:
91 | self.cookie += item.name + '=' + item.value + ';'
92 | if self.cookie == '':
93 | return False
94 | ck_list = self.cookie.replace(" ", "").split(';')
95 | for ph in ck_list:
96 | if "miui_vip_ph=" in ph:
97 | self.miui_vip_ph = ph.replace("miui_vip_ph=", "")
98 | break
99 | return True
100 | except Exception as e:
101 | w_log(e)
102 | return False
103 |
104 | # 浏览帖子10s
105 | def browse_post(self):
106 | headers = {
107 | 'cookie': str(self.cookie)
108 | }
109 | params = {
110 | 'userId': str(self.uid),
111 | 'action': 'BROWSE_POST_10S',
112 | }
113 | try:
114 | response = requests.get('https://api.vip.miui.com/mtop/planet/vip/member/addCommunityGrowUpPointByAction',
115 | params=params, headers=headers)
116 | r_json = response.json()
117 | if r_json['status'] == 401:
118 | return w_log("浏览帖子失败:Cookie无效")
119 | elif r_json['status'] != 200:
120 | return w_log("浏览帖子完成,但有错误:" + str(r_json['message']))
121 | score = r_json['entity']['score']
122 | w_log("浏览帖子完成,成长值+" + str(score))
123 | except Exception as e:
124 | w_log("浏览帖子出错")
125 | w_log(e)
126 |
127 | # 浏览个人主页10s
128 | def browse_user_page(self):
129 | headers = {
130 | 'cookie': str(self.cookie)
131 | }
132 | params = {
133 | 'userId': str(self.uid),
134 | 'action': 'BROWSE_SPECIAL_PAGES_USER_HOME',
135 | }
136 | try:
137 | response = requests.get('https://api.vip.miui.com/mtop/planet/vip/member/addCommunityGrowUpPointByAction',
138 | params=params, headers=headers)
139 | r_json = response.json()
140 | if r_json['status'] == 401:
141 | return w_log("浏览个人主页失败:Cookie无效")
142 | elif r_json['status'] != 200:
143 | return w_log("浏览个人主页完成,但有错误:" + str(r_json['message']))
144 | score = r_json['entity']['score']
145 | w_log("浏览个人主页完成,成长值+" + str(score))
146 | except Exception as e:
147 | w_log("浏览个人主页出错")
148 | w_log(e)
149 |
150 | # 浏览指定专题页
151 | def browse_specialpage(self):
152 | headers = {
153 | 'cookie': str(self.cookie)
154 | }
155 | params = {
156 | 'userId': str(self.uid),
157 | 'action': 'BROWSE_SPECIAL_PAGES_SPECIAL_PAGE',
158 | }
159 | try:
160 | response = requests.get('https://api.vip.miui.com/mtop/planet/vip/member/addCommunityGrowUpPointByAction',
161 | params=params, headers=headers)
162 | r_json = response.json()
163 | if r_json['status'] == 401:
164 | return w_log("浏览专题页失败:Cookie无效")
165 | elif r_json['status'] != 200:
166 | return w_log("浏览专题页完成,但有错误:" + str(r_json['message']))
167 | score = r_json['entity']['score']
168 | w_log("浏览专题页完成,成长值+" + str(score))
169 | except Exception as e:
170 | w_log("浏览专题页出错")
171 | w_log(e)
172 |
173 | # 加入小米圈子
174 | def board_follow(self):
175 | headers = {
176 | 'cookie': str(self.cookie)
177 | }
178 | try:
179 | response = requests.get(
180 | 'https://api.vip.miui.com/api/community/board/follow?'
181 | 'boardId=5462662&pathname=/mio/singleBoard&version=dev.1144',
182 | headers=headers)
183 | r_json = response.json()
184 | if r_json['status'] == 401:
185 | return w_log("加入小米圈子失败:Cookie无效")
186 | elif r_json['status'] != 200:
187 | return w_log("加入小米圈子失败:" + str(r_json['message']))
188 | w_log("加入小米圈子结果:" + str(r_json['message']))
189 | except Exception as e:
190 | w_log("加入小米圈子出错")
191 | w_log(e)
192 |
193 | # 退出小米圈子
194 | def board_unfollow(self):
195 | headers = {
196 | 'cookie': str(self.cookie)
197 | }
198 | try:
199 | response = requests.get('https://api.vip.miui.com/api/community/board/unfollow?'
200 | 'boardId=5462662&pathname=/mio/singleBoard&version=dev.1144', headers=headers)
201 | r_json = response.json()
202 | if r_json['status'] == 401:
203 | return w_log("退出小米圈子失败:Cookie无效")
204 | elif r_json['status'] != 200:
205 | return w_log("退出小米圈子失败:" + str(r_json['message']))
206 | w_log("退出小米圈子结果:" + str(r_json['message']))
207 | except Exception as e:
208 | w_log("退出小米圈子出错")
209 | w_log(e)
210 |
211 | # 社区拔萝卜
212 | def carrot_pull(self):
213 | headers = {
214 | 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
215 | 'cookie': str(self.cookie)
216 | }
217 | data = {
218 | 'miui_vip_ph': str(self.miui_vip_ph)
219 | }
220 | try:
221 | response = requests.post('https://api.vip.miui.com/api/carrot/pull', headers=headers,
222 | data=data)
223 | r_json = response.json()
224 | if r_json['code'] == 401:
225 | return w_log("社区拔萝卜失败:Cookie无效")
226 | elif r_json['code'] != 200:
227 | return w_log("社区拔萝卜失败:" + str(r_json['entity']['message']))
228 | w_log("社区拔萝卜结果:" + str(r_json['entity']['message']))
229 | money_count = r_json['entity']['header']['moneyCount']
230 | w_log("当前金币数:" + str(money_count))
231 | except Exception as e:
232 | w_log("社区拔萝卜出错")
233 | w_log(e)
234 |
235 | # 社区4.0签到
236 | def check_in(self):
237 | headers = {
238 | 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
239 | 'cookie': str(self.cookie)
240 | }
241 | try:
242 | response = requests.get(
243 | 'https://api.vip.miui.com/mtop/planet/vip/user/checkin?pathname=/mio/checkIn&version=dev.1144',
244 | headers=headers)
245 | r_json = response.json()
246 | if r_json['status'] == 401:
247 | return w_log("社区成长值签到失败:Cookie无效")
248 | elif r_json['status'] != 200:
249 | return w_log("社区成长值签到失败:" + str(r_json['message']))
250 | w_log("社区成长值签到结果:成长值+" + str(r_json['entity']))
251 | except Exception as e:
252 | w_log("社区成长值签到出错")
253 | w_log(e)
254 |
255 | # 登录社区App
256 | def login_app(self):
257 | headers = {
258 | 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
259 | 'cookie': str(self.cookie)
260 | }
261 | try:
262 | response = requests.get('https://api.vip.miui.com/mtop/planet/vip/app/init/start/infos', headers=headers)
263 | r_code = response.status_code
264 | if r_code == 401:
265 | return w_log("登录社区App失败:Cookie无效")
266 | elif r_code != 200:
267 | return w_log("登录社区App失败")
268 | w_log("登录社区App成功")
269 | except Exception as e:
270 | w_log("登录社区App出错")
271 | w_log(e)
272 |
273 | def mi_login(self):
274 | proxies = {
275 | 'https': None,
276 | 'http': None
277 | }
278 | headers = {
279 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
280 | 'Referer': 'https://account.xiaomi.com/fe/service/login/password?sid=miui_vip&qs=%253Fcallback%253Dhttp'
281 | '%25253A%25252F%25252Fapi.vip.miui.com%25252Fsts%25253Fsign%25253D4II4ABwZkiJzkd2YSkyEZukI4Ak'
282 | '%2525253D%252526followup%25253Dhttps%2525253A%2525252F%2525252Fapi.vip.miui.com%2525252Fpage'
283 | '%2525252Flogin%2525253FdestUrl%2525253Dhttps%252525253A%252525252F%252525252Fweb.vip.miui.com'
284 | '%252525252Fpage%252525252Finfo%252525252Fmio%252525252Fmio%252525252FinternalTest%252525253Fref'
285 | '%252525253Dhomepage%2526sid%253Dmiui_vip&callback=http%3A%2F%2Fapi.vip.miui.com%2Fsts%3Fsign'
286 | '%3D4II4ABwZkiJzkd2YSkyEZukI4Ak%253D%26followup%3Dhttps%253A%252F%252Fapi.vip.miui.com%252Fpage'
287 | '%252Flogin%253FdestUrl%253Dhttps%25253A%25252F%25252Fweb.vip.miui.com%25252Fpage%25252Finfo'
288 | '%25252Fmio%25252Fmio%25252FinternalTest%25253Fref%25253Dhomepage&_sign=L%2BdSQY6sjSQ%2FCRjJs4p'
289 | '%2BU1vNYLY%3D&serviceParam=%7B%22checkSafePhone%22%3Afalse%2C%22checkSafeAddress%22%3Afalse%2C'
290 | '%22lsrp_score%22%3A0.0%7D&showActiveX=false&theme=&needTheme=false&bizDeviceType=',
291 | 'User-Agent': str(self.user_agent),
292 | 'Origin': 'https://account.xiaomi.com',
293 | 'X-Requested-With': 'XMLHttpRequest',
294 | 'Cookie': 'deviceId=' + str(self.device_id) + '; pass_ua=web; uLocale=zh_CN'
295 | }
296 | data = {
297 | 'bizDeviceType': '',
298 | 'needTheme': 'false',
299 | 'theme': '',
300 | 'showActiveX': 'false',
301 | 'serviceParam': '{"checkSafePhone":false,"checkSafeAddress":false,"lsrp_score":0.0}',
302 | 'callback': 'http://api.vip.miui.com/sts?sign=4II4ABwZkiJzkd2YSkyEZukI4Ak%3D&followup=https%3A%2F%2Fapi.vip'
303 | '.miui.com%2Fpage%2Flogin%3FdestUrl%3Dhttps%253A%252F%252Fweb.vip.miui.com%252Fpage%252Finfo'
304 | '%252Fmio%252Fmio%252FinternalTest%253Fref%253Dhomepage',
305 | 'qs': '%3Fcallback%3Dhttp%253A%252F%252Fapi.vip.miui.com%252Fsts%253Fsign%253D4II4ABwZkiJzkd2YSkyEZukI4Ak'
306 | '%25253D%2526followup%253Dhttps%25253A%25252F%25252Fapi.vip.miui.com%25252Fpage%25252Flogin'
307 | '%25253FdestUrl%25253Dhttps%2525253A%2525252F%2525252Fweb.vip.miui.com%2525252Fpage%2525252Finfo'
308 | '%2525252Fmio%2525252Fmio%2525252FinternalTest%2525253Fref%2525253Dhomepage%26sid%3Dmiui_vip',
309 | 'sid': 'miui_vip',
310 | '_sign': 'L+dSQY6sjSQ/CRjJs4p+U1vNYLY=',
311 | 'user': str(self.uid),
312 | 'cc': '+86',
313 | 'hash': str(self.password),
314 | '_json': 'true'
315 | }
316 | try:
317 | response = requests.post('https://account.xiaomi.com/pass/serviceLoginAuth2', headers=headers, data=data,
318 | proxies=proxies)
319 | response_data = response.text.lstrip('&').lstrip('START').lstrip('&')
320 | r_json = json.loads(response_data)
321 | if r_json['code'] == 70016:
322 | w_log('小米账号登录失败:用户名或密码不正确')
323 | return False
324 | if r_json['code'] != 0:
325 | w_log('小米账号登录失败:' + r_json['desc'])
326 | return False
327 | if r_json['pwd'] != 1:
328 | w_log('当前账号需要短信验证码,请尝试修改UA或设备ID')
329 | return False
330 | if not self.get_vip_cookie(r_json['location']):
331 | w_log('小米账号登录成功,社区获取 Cookie 失败')
332 | return False
333 | w_log('账号登录完成')
334 | return True
335 | except Exception as e:
336 | w_log("登录小米账号出错")
337 | w_log(e)
338 | return False
339 |
340 | def get_point(self) -> int:
341 | """
342 | 这个方法带返回值的原因是,可以调用这个方法获取返回值,可根据这个方法定制自己的“消息提示功能”。
343 | 如:Qmsg发送到QQ 或者 发送邮件提醒
344 | :return: 当前的成长值
345 | """
346 | headers = {
347 | 'cookie': str(self.cookie)
348 | }
349 | try:
350 | response = requests.get('https://api.vip.miui.com/mtop/planet/pc/post/userInfo', headers=headers)
351 | r_json = response.json()
352 | your_point = r_json['entity']['userGrowLevelInfo']['point']
353 | w_log('成功获取成长值,当前成长值:' + str(your_point))
354 | return your_point
355 | except Exception as e:
356 | w_log('成长值获取失败')
357 | process_exception(e)
358 |
359 |
360 | def process_exception(e: Exception):
361 | """
362 | 全局异常处理
363 | :param e: 异常实例
364 | :return: No return
365 | """
366 | if e.__str__() == 'check_hostname requires server_hostname':
367 | w_log('系统设置了代理,出现异常')
368 |
369 |
370 | def start(miui_task: MIUITask, check_in: bool, carrot_pull: bool, browse_specialpage: bool):
371 | if miui_task.mi_login():
372 | w_log("本脚本支持社区拔萝卜及成长值签到,因该功能存在风险默认禁用")
373 | w_log("如您愿意承担一切可能的后果,可编辑配置文件手动打开该功能")
374 | miui_task.login_app()
375 | if carrot_pull:
376 | w_log("风险功能提示:正在进行社区拔萝卜")
377 | random_sleep()
378 | miui_task.carrot_pull()
379 | if check_in:
380 | w_log("风险功能提示:正在进行成长值签到")
381 | random_sleep()
382 | miui_task.check_in()
383 | if browse_specialpage:
384 | w_log("风险功能提示:正在完成浏览专题页10s任务")
385 | sleep_ten_sec_more()
386 | miui_task.browse_specialpage()
387 | w_log("正在完成浏览帖子10s任务,第一次")
388 | sleep_ten_sec_more()
389 | miui_task.browse_post()
390 | w_log("正在完成浏览帖子10s任务,第二次")
391 | sleep_ten_sec_more()
392 | miui_task.browse_post()
393 | w_log("正在完成浏览帖子10s任务,第三次")
394 | sleep_ten_sec_more()
395 | miui_task.browse_post()
396 | w_log("正在完成点赞任务")
397 | miui_task.thumb_up()
398 | random_sleep()
399 | miui_task.cancel_thumb_up()
400 | random_sleep()
401 | miui_task.thumb_up()
402 | random_sleep()
403 | miui_task.cancel_thumb_up()
404 | random_sleep()
405 | miui_task.thumb_up()
406 | random_sleep()
407 | miui_task.cancel_thumb_up()
408 | random_sleep()
409 | miui_task.board_unfollow()
410 | random_sleep()
411 | miui_task.board_follow()
412 | random_sleep()
413 | miui_task.browse_user_page()
414 | random_sleep()
415 | miui_task.get_point()
416 |
417 |
418 | def main():
419 | w_log("MIUI-AUTO-TASK v1.5.2")
420 | w_log('---------- 系统信息 -------------')
421 | system_info()
422 | w_log('---------- 项目信息 -------------')
423 | w_log("项目地址:https://github.com/0-8-4/miui-auto-tasks")
424 | w_log("欢迎 star,感谢東雲研究所中的大佬")
425 | w_log('---------- 配置检测 -------------')
426 |
427 | config = get_config()
428 |
429 | if not check_config(config):
430 | w_log('配置文件没有正确配置')
431 | exit(1)
432 | else:
433 | config = format_config(config)
434 |
435 | for i in config.get('accounts'):
436 | w_log('---------- EXECUTING -------------')
437 | start(
438 | MIUITask(i.get('uid'), i.get('password'), i.get('user-agent'), device_id=i.get('device-id')),
439 | i.get('check-in'), i.get('carrot-pull'), i.get('browse-specialpage'),
440 | )
441 | time.sleep(5)
442 | s_log(config.get('logging'))
443 |
444 |
445 | def main_handler(event, context):
446 | main()
447 |
448 |
449 | if __name__ == "__main__":
450 | main()
451 |
--------------------------------------------------------------------------------
/MIUI/config.yaml:
--------------------------------------------------------------------------------
1 | accounts:
2 | - uid: 100001
3 | # 小米账户ID 非小米账户用户名
4 | password: abc123
5 | # 小米账户密码或其MD5哈希
6 | user-agent: 'Mozilla/5.0 (Linux; U; Android 11; zh-cn; M2007J1SC Build/RKQ1.200826.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.116 Mobile Safari/537.36 XiaoMi/MiuiBrowser/15.7.22 app/vipaccount'
7 | # 登录小米社区时所用浏览器的 User-Agent
8 | # 可在此工具查看:https://tool.chinaz.com/useragent
9 | carrot-pull: false
10 | # 小米社区拔萝卜,可能存在封号风险
11 | check-in: true
12 | # 小米社区成长值签到,可能存在封号风险
13 | # 本脚本默认完成登录社区和三次点赞及三次浏览十秒帖子的成长值任务
14 | browse-post: 1
15 | # 浏览十秒帖子
16 | browse-specialpage: false
17 | # 小米社区在活动期间可能会出现限时的“浏览指定专题页”任务
18 | # 考虑到安全问题,建议在存在该限时活动手动打开,活动结束时关闭
19 | thumb-up: 1
20 | # 小米社区点赞
21 | board-follow: false
22 | # 小米社区加入小米圈子
23 | # 若有多个小米账户,按照以下模板进行修改,使用时删除前端 #注释
24 | # - uid: 100001
25 | # password: abc123
26 | # user-agent: 'Mozilla/5.0 (Android 11; Mobile; rv:95.0) Gecko/95.0 Firefox/95.0'
27 | # carrot-pull: false
28 | # check-in: false
29 | # browse-post: 3
30 | # browse-specialpage: false
31 | # thumb-up: 3
32 | # board-follow: true
33 | logging: false
34 | # 归档日志到本地文件
35 | version: v1.5.4
36 | # config 文件版本号,debug用
--------------------------------------------------------------------------------
/MIUI/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import random
3 | import time
4 | import platform
5 | import dotenv, yaml
6 |
7 | from hashlib import md5
8 | from urllib.request import getproxies
9 |
10 | logs = ''
11 | CONFIG_VERSION_REQUIRE: str = 'v1.5.2'
12 |
13 |
14 | def md5_crypto(passwd: str) -> str:
15 | return md5(passwd.encode('utf8')).hexdigest()
16 |
17 |
18 | def show_info(tip, info):
19 | return "{}: {}".format(tip, info)
20 |
21 |
22 | def system_info():
23 | w_log(show_info('操作系统平台', platform.platform()))
24 | w_log(show_info('操作系统版本', platform.version()))
25 | w_log(show_info('操作系统名称', platform.system()))
26 | w_log(show_info('操作系统位元', platform.architecture()))
27 | w_log(show_info('操作系统类型', platform.machine()))
28 | w_log(show_info('处理器信息', platform.processor()))
29 | w_log(show_info('Python 版本', str(platform.python_version()) + ' ' + str(platform.python_build())))
30 | if getproxies():
31 | w_log(show_info('系统代理', getproxies()))
32 |
33 |
34 | def get_config() -> dict:
35 | config = {'account': []}
36 | config_path_legacy = dotenv.find_dotenv(filename='config.env')
37 | config_path_yaml = dotenv.find_dotenv(filename='config.yaml')
38 |
39 | # the old legacy config
40 | if config_path_legacy:
41 | w_log('正在使用 ' + config_path_legacy + ' 作为配置文件')
42 | legacy_config = dotenv.dotenv_values(config_path_legacy)
43 | config['account'].append({'uid': legacy_config.get('MI_ID')})
44 | config['account'][0]['password'] = legacy_config.get('MI_PASSWORD')
45 | config['account'][0]['user-agent'] = legacy_config.get('USER_AGENT')
46 | if legacy_config.get('SIGN_IN') and legacy_config.get('SIGN_IN').upper() in ('Y', 'YES'):
47 | config['account'][0]['check-in'] = True
48 | else:
49 | config['account'][0]['check-in'] = False
50 | if legacy_config.get('CARROT_PULL') and legacy_config.get('CARROT_PULL').upper() in ('Y', 'YES'):
51 | config['account'][0]['carrot-pull'] = True
52 | else:
53 | config['account'][0]['carrot-pull'] = False
54 | if legacy_config.get('BROWSE_SPECIALPAGE') and legacy_config.get('BROWSE_SPECIALPAGE').upper() in ('Y', 'YES'):
55 | config['account'][0]['browse-specialpage'] = True
56 | else:
57 | config['account'][0]['browse-specialpage'] = False
58 | if legacy_config.get('LOG_SAVE') and legacy_config.get('LOG_SAVE').upper() in ('Y', 'YES'):
59 | config['logging'] = True
60 | else:
61 | config['logging'] = False
62 | return config
63 |
64 | # the new version yaml config
65 | elif config_path_yaml:
66 | w_log('正在加载 ' + config_path_yaml + ' 配置文件')
67 | with open(config_path_yaml, "rb") as stream:
68 | try:
69 | config = yaml.safe_load(stream)
70 | config_version: str = config.get('version')
71 |
72 | # check config file version
73 | # if config version not meet the requirement
74 | if CONFIG_VERSION_REQUIRE != config_version:
75 | w_log('配置文件版本和程序运行要求版本不匹配,请检查配置文件')
76 | w_log('配置文件版本: ' + config_version)
77 | w_log('运行程序配置版本要求: ' + CONFIG_VERSION_REQUIRE)
78 | exit(1) # exit the program
79 | w_log('配置文件已成功加载,文件版本 ' + config_version)
80 |
81 | except yaml.YAMLError as e:
82 | w_log('配置文件载入错误')
83 | w_log(e)
84 | return config
85 | else:
86 | w_log('配置文件不存在')
87 | exit(1)
88 |
89 |
90 | def w_log(text):
91 | global logs
92 | now_localtime = time.strftime("%H:%M:%S", time.localtime())
93 | logs += now_localtime + ' | ' + str(text) + '\n'
94 | print(now_localtime + ' | ' + str(text))
95 |
96 |
97 | def s_log(flag):
98 | if flag:
99 | global logs
100 | folder = os.path.exists('./logs')
101 | if not folder:
102 | os.makedirs('./logs')
103 | now_localtime = time.strftime("%Y-%m-%d", time.localtime())
104 | fname = now_localtime + '.log'
105 | with open('./logs/' + fname, 'a+', encoding='utf-8') as f:
106 | f.write(logs)
107 |
108 |
109 | def check_config(config: dict) -> bool:
110 | if config.get('accounts'):
111 | for i in config.get('accounts'):
112 | if not i.get('uid') or not i.get('password') or not i.get('user-agent'):
113 | return False
114 | if not isinstance(i.get('check-in'), bool):
115 | return False
116 | if not isinstance(i.get('carrot-pull'), bool):
117 | return False
118 | if not isinstance(i.get('browse-specialpage'), bool):
119 | return False
120 | else:
121 | return False
122 | if not isinstance(config.get('logging'), bool):
123 | return False
124 | return True
125 |
126 |
127 | def format_config(config: dict) -> dict:
128 | for i in config.get('accounts'):
129 | i['uid'] = str(i.get('uid'))
130 | i['user-agent'] = str(i.get('user-agent'))
131 | if len(i.get('password')) != 32:
132 | i['password'] = md5_crypto(i.get('password')).upper()
133 | else:
134 | i['password'] = str(i.get('password')).upper()
135 | if i.get('device-id'):
136 | i['device-id'] = str(i.get('device-id'))
137 | else:
138 | i['device-id'] = None
139 | return config
140 |
141 |
142 | def random_sleep():
143 | time.sleep(random.randint(1, 9))
144 |
145 |
146 | def sleep_ten_sec_more():
147 | time.sleep(random.randint(10, 12))
148 |
--------------------------------------------------------------------------------
/QQSpeed/dig_treasure.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | """
4 | new Env('掌上飞车寻宝')
5 | cron: 0 2 0,12 * * *
6 | Author : SmallBaby
7 | Date : 2022-12-19 09:00:00
8 | LastEditTime : 2023-01-09 13:00:00
9 | FilePath : QingLongScript/QQSpeed/dig_treasure.py
10 | Description :
11 | 添加环境变量QQ_SPEED_COOKIE、QQ_SPEED_REFERER,多账号用回车换行分开
12 | 值分别是cookie和referer
13 | """
14 | import json
15 | import random
16 | import threading
17 | import time
18 | from os import environ
19 | from urllib.parse import unquote
20 | from bs4 import BeautifulSoup
21 | import requests
22 | import re
23 | import sys
24 |
25 | sys.path.append('../')
26 |
27 | try:
28 | import Utils.notify
29 | except Exception as err: # 异常捕捉
30 | print(f'{err}\n加载通知服务失败~\n')
31 |
32 |
33 | # 测试用环境变量
34 | # environ['QQ_SPEED_COOKIE'] = ''
35 | # environ['QQ_SPEED_REFERER'] = ''
36 |
37 |
38 | class DigTreasure:
39 | def __init__(self, cookie, referer, star_id=0):
40 | self.cookie = cookie
41 | self.referer = f'{baseHost}?{referer.split("?")[1]}'
42 | self.headers = get_headers(self.cookie, self.referer)
43 | self.userInfo, self.mapInfo = self.get_treasure_index()
44 | self.userData = self.get_user_data()
45 | self.todayTimes = int(self.userInfo.get('todayTimes'))
46 | self.mapEvent = self.get_map_event(star_id)
47 | self.msg = f'账号{self.userData.get("roleId")}寻宝结果:\n'
48 |
49 | def get_user_data(self):
50 | user_data = {}
51 | for kv in self.referer.split('?')[1].split('&'):
52 | if len(kv) > 0:
53 | kvArr = kv.split('=')
54 | key = kvArr[0]
55 | value = unquote(kvArr[1]) if len(kvArr) == 2 else ''
56 | user_data.update({key: value})
57 | return user_data
58 |
59 | # 获取寻宝地图
60 | def get_map_event(self, star_id):
61 | starId = mapId = star_id
62 | if starId == 0:
63 | # 获取startId
64 | starInfo = self.userInfo.get('starInfo')
65 | starKeys = list(starInfo.keys())
66 | starKeys.reverse()
67 | for star in starKeys:
68 | if starInfo.get(star) == 1:
69 | starId = star
70 | break
71 |
72 | # 获取mapId
73 | maps = self.mapInfo.get(starId)
74 | for mapObj in maps:
75 | if mapObj.get('isdaji') == 1:
76 | mapId = mapObj.get('id')
77 | break
78 |
79 | return {
80 | 'starId': int(starId),
81 | 'mapId': mapId,
82 | 'type': 1 if self.userInfo.get('vip_flag') == 0 else 2
83 | }
84 |
85 | # 获取userInfo / mapInfo
86 | def get_treasure_index(self):
87 | headers = self.headers
88 | res = requests.get(self.referer, headers=headers)
89 | htmlStr = res.text
90 | html = BeautifulSoup(htmlStr, 'html.parser')
91 | pattern = re.compile(r"eval\('\((.*?)\)'\);", re.MULTILINE | re.DOTALL)
92 | evalScript = html.find('script', text=pattern)
93 | userInfo = json.loads(re.findall(pattern, evalScript.text)[0])
94 | mapInfo = json.loads(re.findall(pattern, evalScript.text)[1])
95 | return userInfo, mapInfo
96 |
97 | # 鉴权汇报
98 | def login_analysis(self, eid=10272):
99 | headers = self.headers
100 | url = 'https://bang.qq.com/login/analysis'
101 | params = {
102 | 'mid': 10,
103 | 'eid': eid,
104 | 'game': 'speed',
105 | 'uin': self.userData.get('uin'),
106 | 'openid': self.userData.get('appOpenid'),
107 | 'userid': self.userData.get('userId'),
108 | 'surl': '',
109 | 'durl': '/app/speed/treasure/index',
110 | 'qq': self.userData.get('roleId'),
111 | 'aid': self.userData.get('userId'),
112 | 'from': 'mobile',
113 | 'ref': ''
114 | }
115 | res = requests.get(url, headers=headers, params=params).json()
116 | return res.get('res') == 0
117 |
118 | # 开始寻宝
119 | def do_dig_treasure(self, action='start'):
120 | headers = self.headers
121 | url = f'https://bang.qq.com/app/speed/treasure/ajax/{action}DigTreasure'
122 | data = {
123 | 'game': 'speed',
124 | 'mapId': self.mapEvent.get('mapId'),
125 | 'starId': self.mapEvent.get('starId'),
126 | 'type': self.mapEvent.get('type'),
127 | 'serverId': self.userData.get('serverId'),
128 | 'areaId': self.userData.get('areaId'),
129 | 'roleId': self.userData.get('roleId'),
130 | 'userId': self.userData.get('userId'),
131 | 'appOpenid': self.userData.get('appOpenid'),
132 | 'uin': self.userData.get('uin'),
133 | 'token': self.userData.get('token'),
134 | }
135 | res = requests.post(url, headers=headers, data=data).json()
136 | isSuccess = res.get('res') == 0
137 | if isSuccess:
138 | isSuccess = self.login_analysis(10273 if action == 'start' else 10274)
139 | if isSuccess and action == 'end':
140 | self.todayTimes += 1
141 | self.get_amesvr_info()
142 | self.get_amesvr_info(1)
143 | return isSuccess
144 |
145 | # AMS活动获奖详情
146 | def get_amesvr_info(self, action=0):
147 | flowIdMap = {
148 | # starId: [M, B]
149 | '1': [856152, 856155],
150 | '2': [856155, 856157],
151 | '3': [856158, 856159],
152 | '4': [856160, 856161],
153 | '5': [856162, 856163],
154 | '6': [856164, 856165],
155 | }
156 | # 参数获取: https://apps.game.qq.com/comm-htdocs/js/ams/actDesc/228/468228/act.desc.js
157 | headers = get_headers(self.cookie, 'https://bang.qq.com')
158 | url = 'https://act.game.qq.com/ams/ame/amesvr'
159 | flowId = flowIdMap.get(str(self.mapEvent.get('starId')))[action]
160 | params = {
161 | 'ameVersion': 0.3,
162 | 'sServiceType': 'bb',
163 | 'iActivityId': '468228',
164 | 'sServiceDepartment': 'xinyue',
165 | 'sSDID': '42a6eb3c5e2fec32f90c3b085368457a',
166 | 'sMiloTag': f'AMS-MILO-468228-{flowId}-{self.userData.get("appOpenid")}-{int(time.time() * 1000)}-{get_random_sign()}',
167 | '_': int(time.time() * 1000)
168 | }
169 | data = {
170 | 'appid': self.userData.get('appid'),
171 | 'gameId': '',
172 | 'sArea': self.userData.get('areaId'),
173 | 'iSex': '',
174 | 'sRoleId': self.userData.get('roleId'),
175 | 'iGender': '',
176 | 'openid': '',
177 | 'accessToken': self.userData.get('accessToken'),
178 | 'sPartition': self.userData.get('areaId'),
179 | 'sAreaName': self.userData.get('areaName'),
180 | 'sRoleName': self.userData.get('roleName'),
181 | 'starid': self.mapEvent.get('starId'),
182 | 'mapid': self.mapEvent.get('mapId'),
183 | 'xhr': 1,
184 | 'sServiceType': 'bb',
185 | 'objCustomMsg': '',
186 | 'areaname': '',
187 | 'roleid': '',
188 | 'rolelevel': '',
189 | 'rolename': '',
190 | 'areaid': '',
191 | 'iActivityId': '468228',
192 | 'iFlowId': flowId,
193 | 'sServiceDepartment': 'xinyue',
194 | # 参数获取 FlowEngine: https://ossweb-img.qq.com/images/js/mobile_bundle/ams/flowengine.js?1662566021380
195 | # MILO地址: https://ossweb-img.qq.com/images/js/mobile_bundle/milo.js
196 | # 参数获取 AMSEngine: https://ossweb-img.qq.com/images/js/mobile_bundle/ams/engine.js?1662566021380
197 | # 参数获取 EAS: https://ossweb-img.qq.com/images/js/eas/eas.js
198 | 'g_tk': get_ame_token(self.userData.get('skey', 'a1b2c3')),
199 | 'e_code': self.userData.get('e_code', 0),
200 | 'g_code': self.userData.get('g_code', 0),
201 | 'eas_url': get_eas_url(self.referer),
202 | 'eas_refer': get_eas_refer()
203 | }
204 | res = requests.post(url, headers=headers, params=params, data=data)
205 | res.encoding = res.apparent_encoding
206 | res = res.json()
207 | # print(f'抽奖详情: {res}\n')
208 | if int(res.get('ret')) == 0:
209 | packageName = res.get('modRet').get('sPackageName')
210 | if action == 0 and len(packageName) > 0:
211 | self.set_speed_actlb_info(res.get('modRet').get('iPackageId'))
212 | print(f'{self.userData.get("roleId")}寻宝结束,获得了{packageName}\n')
213 | print(f'{res.get("modRet").get("sMsg")}\n')
214 | self.msg += f'第{self.todayTimes}次寻宝结束,获得了{packageName}\n'
215 | else:
216 | print(f'{self.userData.get("roleId")}: {res.get("msg")}\n')
217 |
218 | # 设置寻宝信息
219 | def set_speed_actlb_info(self, package_id):
220 | headers = self.headers
221 | packageIds = ['3226026', '3226028', '3226073', '3226115', '3226119', '3226139', '3226140', '3226141', '3226183',
222 | '3226184', '3226208', '3226209', '3226213', '3226226', '3226227', '3226225', '3226245']
223 | url = 'https://bang.qq.com/app/activity/setSpeedActlbInfo'
224 | data = {
225 | 'uin': self.userData.get('uin'),
226 | 'lbId': package_id,
227 | 'rolename': self.userData.get('rolename'),
228 | 'userId': self.userData.get('userId'),
229 | 'token': self.userData.get('token'),
230 | 'gameId': self.userData.get('gameId')
231 | }
232 | if package_id in packageIds:
233 | res = requests.post(url, headers=headers, data=data).json()
234 | print(res)
235 |
236 | def main(self):
237 | userId = self.userData.get('roleId')
238 | print(f'线程-{userId} 创建成功\n')
239 | todayCanTimes = int(self.userInfo.get('todaycanTimes'))
240 | isLogin = self.login_analysis()
241 | userDelay = 10 if self.mapEvent.get('type') == 2 else 10 * 60
242 | for idx in range(todayCanTimes - self.todayTimes):
243 | print(f'{userId}今日寻宝总次数:{todayCanTimes},现在进行第{self.todayTimes + 1}次寻宝\n')
244 | if isLogin:
245 | isStart = self.do_dig_treasure()
246 | print(f'{userId}寻宝已经开始,将在{userDelay}秒后结束寻宝\n')
247 | if isStart:
248 | threadEnd = threading.Timer(userDelay, DigTreasure.do_dig_treasure, args=(self, 'end'))
249 | threadEnd.start()
250 | threadEnd.join()
251 | if self.todayTimes == todayCanTimes:
252 | print(f'{userId}今日寻宝已结束,共寻宝{todayCanTimes}次\n')
253 | self.msg += f'{userId}今日寻宝已结束,共寻宝{todayCanTimes}次\n'
254 | try:
255 | notify.send('掌上飞车寻宝', self.msg)
256 | except Exception as err: # 异常捕捉
257 | print(f'{err}\n加载通知服务失败~\n')
258 | time.sleep(5)
259 |
260 |
261 | # 构造请求头
262 | def get_headers(cookie='', referer=''):
263 | headers = {
264 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; Redmi K20 Pro Premium Edition Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.105 Mobile Safari/537.36 GH_QQConnect GameHelper_1003/2103040778',
265 | 'Connection': 'keep-alive',
266 | 'Accept': 'application/json',
267 | 'Accept-Encoding': 'gzip, deflate, br',
268 | 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
269 | }
270 | if cookie != '':
271 | headers['Cookie'] = cookie
272 | if referer != '':
273 | if '?' in referer:
274 | headers['Referer'] = f'{baseHost}?{referer.split("?")[1]}'
275 | else:
276 | headers['Referer'] = referer
277 | return headers
278 |
279 |
280 | # 获取Host并格式化
281 | def get_eas_url(referer):
282 | if isinstance(referer, str):
283 | referer = referer.lower()
284 | if referer.find('?') != -1:
285 | referer = referer[:referer.find('?')]
286 | if referer.find('#') != -1:
287 | referer = referer[:referer.find('#')]
288 | strArr = referer.split('/')
289 | if len(strArr[-1]) > 0 and strArr[-1].find('.shtml') == -1 and strArr[-1].find('.html') == -1 and strArr[
290 | -1].find('.htm') == -1 and strArr[-1].find('.php') == -1:
291 | referer += '/'
292 | referer = re.compile(r"\\+").sub('/', referer)
293 | referer = re.compile(r"^https?:/+").sub('http://', referer)
294 | referer = re.compile(r"^/").sub('http://', referer)
295 | return referer
296 | return ''
297 |
298 |
299 | # 生成G_TK
300 | def get_ame_token(ame_str):
301 | hashVal = 5381
302 | for h in range(len(ame_str)):
303 | hashVal += (hashVal << 5) + ord(ame_str[h])
304 | return hashVal & 2147483647
305 |
306 |
307 | # 生成EAS referer参数
308 | def get_eas_refer(referer=''):
309 | # 示例: http://noreferrer/?reqid=781b2c57-a30b-4827-8faa-d5cb5e2d6f42&version=26
310 | easVersion = 26
311 | easRefer = referer
312 | if len(easRefer) == 0:
313 | easRefer = 'http://noreferrer/'
314 | easRefer = get_eas_url(easRefer)
315 | if easRefer.find('?') == -1:
316 | return f'{easRefer}?reqid={get_uuid()}&version={easVersion}'
317 | return f'{easRefer}&reqid={get_uuid()}&version={easVersion}'
318 |
319 |
320 | # 生成随机sign
321 | def get_random_sign():
322 | randomMask = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
323 | randomSign = ''
324 | while len(randomSign) < 6:
325 | rIndex = random.randint(0, 61)
326 | randomSign += randomMask[rIndex]
327 | return randomSign
328 |
329 |
330 | # 生成UUID
331 | def get_uuid():
332 | uuidMask = '0123456789abcdef'
333 | uuid = [''] * 36
334 | for u in range(36):
335 | uuid[u] = uuidMask[random.randint(0, 15)]
336 | uuid[14] = '4'
337 | uuid[19] = uuidMask[(0 if int(uuid[19], 16) > 9 else int(uuid[19])) & 3 | 8]
338 | uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
339 | uuid[35] = uuidMask[
340 | (ord(uuid[0]) + ord(uuid[10]) + ord(uuid[16]) + ord(uuid[22]) + ord(uuid[28])) % len(uuidMask)]
341 | uuid[34] = uuidMask[((ord(uuid[1]) + ord(uuid[11]) + ord(uuid[17]) + ord(uuid[29])) % len(uuidMask))]
342 | return ''.join(uuid)
343 |
344 |
345 | # 读取环境变量并格式化
346 | def get_cookie():
347 | cookie_arr = get_environ('QQ_SPEED_COOKIE').split('\n')
348 | referer_arr = get_environ('QQ_SPEED_REFERER').split('\n')
349 | return cookie_arr, referer_arr
350 |
351 |
352 | # 封装读取环境变量的方法
353 | def get_environ(key, default=''):
354 | if len(environ.get(key, '')) == 0:
355 | print(f'环境变量 {key} 未添加,程序已退出\n')
356 | exit()
357 | return environ.get(key, default)
358 |
359 |
360 | def run_threading(cookie, referer):
361 | DigTreasure(cookie, referer).main()
362 |
363 |
364 | if __name__ == '__main__':
365 | baseHost = 'https://bang.qq.com/app/speed/treasure/index'
366 | cookieArr, refererArr = get_cookie()
367 | threadArr = []
368 | for i in range(len(cookieArr)):
369 | if len(refererArr) < i + 1:
370 | print('填写了Cookie,但没填写Referer,跳过该账号\n')
371 | continue
372 | threadUser = threading.Thread(target=run_threading, args=(cookieArr[i], refererArr[i]))
373 | threadUser.start()
374 | threadArr.append(threadUser)
375 | for i in range(len(cookieArr)):
376 | threadArr[i].join()
377 |
--------------------------------------------------------------------------------
/QQSpeed/jinsilou.py:
--------------------------------------------------------------------------------
1 | '''
2 | new Env('掌上飞车-0点开30个金丝篓')
3 | cron: 0 0 * * *
4 | Author : BNDou
5 | Date : 2022-12-28 23:58:11
6 | LastEditTime : 2022-12-30 22:52:22
7 | FilePath : /Auto_Check_In/checkIn_ZhangFei_JinSiLou.py
8 | Description : 金丝篓开永久雷诺
9 | 添加环境变量COOKIE_ZHANGFEI、REFERER_ZHANGFEI,多账号用回车换行分开
10 | 值分别是cookie和referer
11 | '''
12 | from urllib.parse import unquote
13 | import requests
14 | import os
15 | import sys
16 | sys.path.append('.')
17 | requests.packages.urllib3.disable_warnings()
18 |
19 | # 测试用环境变量
20 | os.environ['COOKIE_ZHANGFEI'] = 'access_token=3589300F3A3A168F8967E637BCA2ABE3; appOpenId=2CFA4BE9C08A7C8D813BF06D2BA09972; appOpenid=2CFA4BE9C08A7C8D813BF06D2BA09972; openId=2CFA4BE9C08A7C8D813BF06D2BA09972; openid=2CFA4BE9C08A7C8D813BF06D2BA09972; appid=1105330667; appId=1105330667; uin=o0948840188; accessToken=3589300F3A3A168F8967E637BCA2ABE3; acctype=qc; access_token=3589300F3A3A168F8967E637BCA2ABE3; appOpenId=2CFA4BE9C08A7C8D813BF06D2BA09972; appOpenid=2CFA4BE9C08A7C8D813BF06D2BA09972; openId=2CFA4BE9C08A7C8D813BF06D2BA09972; openid=2CFA4BE9C08A7C8D813BF06D2BA09972; appid=1105330667; appId=1105330667; uin=o0948840188; accessToken=3589300F3A3A168F8967E637BCA2ABE3; acctype=qc; 300969a6ff940bbdfd62ab232499eedd=1; access_token=3589300F3A3A168F8967E637BCA2ABE3; openid=2CFA4BE9C08A7C8D813BF06D2BA09972; appid=1105330667; acctype=qc; eas_sid=R1t6Z7J3p2e2y5D472c8X5g4m7'
21 | os.environ['REFERER_ZHANGFEI'] = 'https://bang.qq.com/app/speed/treasure/index?toOpenid=&serverName=&toUin=948840188&cGameId=1003&serverId=0&gameName=QQ%E9%A3%9E%E8%BD%A6%E7%AB%AF%E6%B8%B8&areaName=%E7%94%B5%E4%BF%A1%E5%8C%BA&nickname=%E6%95%99%E4%B8%BB&uin=948840188&roleLevel=201&accType=qc&gameId=10013&roleId=948840188&uniqueRoleId=173393807&avatar=http%3A%2F%2Fq.qlogo.cn%2Fqqapp%2F1104466820%2F57637BA7E695AFC3692326569BE55ED5%2F100&accessToken=3589300F3A3A168F8967E637BCA2ABE3&userId=328733790&token=8bUWmBJm&isMainRole=1&subGameId=10013&appOpenid=2CFA4BE9C08A7C8D813BF06D2BA09972&areaId=1&roleJob=&appid=1105330667&roleName=My%E4%B8%A8%E7%81%ACDear%E4%B8%BF%E6%89%B6%E6%91%87&_isShare=1&'
22 |
23 | try: # 异常捕捉
24 | from sendNotify import send # 导入消息通知模块
25 | except Exception as err: # 异常捕捉
26 | print('%s\n加载通知服务失败~' % err)
27 |
28 |
29 | # 获取环境变量
30 | def get_env():
31 | # 判断 COOKIE_ZHANGFEI是否存在于环境变量
32 | if "COOKIE_ZHANGFEI" in os.environ:
33 | # 读取系统变量 以 \n 分割变量
34 | cookie_list = os.environ.get('COOKIE_ZHANGFEI').split('\n')
35 | # 判断 cookie 数量 大于 0 个
36 | if len(cookie_list) <= 0:
37 | # 标准日志输出
38 | print('COOKIE_ZHANGFEI变量未启用')
39 | send('掌上飞车开金丝篓', 'COOKIE_ZHANGFEI变量未启用')
40 | # 脚本退出
41 | sys.exit(1)
42 | else:
43 | # 标准日志输出
44 | print('未添加COOKIE_ZHANGFEI变量')
45 | send('掌上飞车开金丝篓', '未添加COOKIE_ZHANGFEI变量')
46 | # 脚本退出
47 | sys.exit(0)
48 |
49 | # 判断 REFERER_ZHANGFEI是否存在于环境变量
50 | if "REFERER_ZHANGFEI" in os.environ:
51 | referer_list = os.environ.get('REFERER_ZHANGFEI').split('\n')
52 | if len(referer_list) <= 0:
53 | print('REFERER_ZHANGFEI变量未启用')
54 | send('掌上飞车开金丝篓', 'REFERER_ZHANGFEI变量未启用')
55 | sys.exit(1)
56 | else:
57 | print('未添加REFERER_ZHANGFEI变量')
58 | send('掌上飞车开金丝篓', '未添加REFERER_ZHANGFEI变量')
59 | sys.exit(0)
60 |
61 | return cookie_list, referer_list
62 |
63 |
64 | # 开箱子
65 | def openBox(cookie, user_data):
66 | msg = ''
67 | s = requests.Session()
68 | s.headers.update({'User-Agent': 'Mozilla/5.0 (Linux; Android 12; Mi 10 Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/96.0.4664.104 Mobile Safari/537.36 GH_QQConnect GameHelper_1003/2103040778'})
69 |
70 | url = "https://bang.qq.com/app/speed/chest/ajax/openBoxByKey"
71 | headers = {
72 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 12; Mi 10 Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/96.0.4664.104 Mobile Safari/537.36 GH_QQConnect GameHelper_1003/2103040778',
73 | 'Connection': 'keep-alive',
74 | 'Accept': 'application/json',
75 | 'Accept-Encoding': 'gzip, deflate, br',
76 | 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
77 | 'Referer': f"https://bang.qq.com/app/speed/chest/index/v2?uin={user_data.get('roleId')}&roleId={user_data.get('roleId')}&uniqueRoleId={user_data.get('uniqueRoleId')}&accessToken={user_data.get('accessToken')}&userId={user_data.get('userId')}&token={user_data.get('token')}&areaId={user_data.get('areaId')}&",
78 | 'Cookie': cookie
79 | }
80 |
81 | # 生成表单
82 | data = {
83 | 'userId': user_data.get('userId'), # 掌飞id
84 | 'uin': user_data.get('roleId'), # QQ账号
85 | 'areaId': user_data.get('areaId'), # 大区
86 | 'token': user_data.get('token'), # 令牌
87 | 'keyId1': '17456', # 大闸蟹17456
88 | 'keyNum1': '2', # 1个金丝篓开2个大闸蟹
89 | 'boxId': '17455', # 金丝篓17455
90 | 'openNum': '1' # 1个金丝篓开2个大闸蟹
91 | }
92 |
93 | # 延迟2秒执行,防止频繁
94 | # time.sleep(2)
95 |
96 | r = s.post(url=url, data=data, headers=headers)
97 | a = r.json()
98 | # 是否成功
99 | if 'data' in a:
100 | if 'itemList' in a.get('data'):
101 | itemList = a.get('data').get('itemList')
102 | num = 0
103 | for num in range(len(itemList)):
104 | msg += f"{itemList[num].get('avtarname')} * {itemList[num].get('num')} "
105 | print(
106 | f"{itemList[num].get('avtarname')} * {itemList[num].get('num')}")
107 | num += 1
108 |
109 | if 'msg' in a.get('data'):
110 | msg += a.get('data').get('msg')
111 | print(a.get('data').get('msg'))
112 |
113 | return msg
114 |
115 |
116 | def main(*arg):
117 | msg = ""
118 | sendnoty = 'true'
119 | global cookie_zhangfei
120 | global referer_zhangfei
121 | cookie_zhangfei, referer_zhangfei = get_env()
122 |
123 | i = 0
124 | while i < len(cookie_zhangfei):
125 | # 获取user_data参数
126 | user_data = {}
127 | for a in referer_zhangfei[i].split('?')[1].split('&'):
128 | if len(a) > 0:
129 | user_data.update(
130 | {a.split('=')[0]: unquote(a.split('=')[1])})
131 | # print(user_data)
132 |
133 | # 开始任务
134 | log = f"第 {i+1} 个账号 {user_data.get('uin')} {user_data.get('roleName')} 开始执行任务"
135 | msg += log + '\n'
136 | print(log)
137 |
138 | # 开金丝篓 * 30
139 | num = 0
140 | for num in range(1):
141 | log = f"开第{num+1}个:"
142 | msg += log
143 | print(log)
144 |
145 | # 开箱子
146 | log = openBox(cookie_zhangfei[i].replace(' ', ''), user_data)
147 | msg += log + '\n'
148 | if '雷诺' in msg:
149 | msg += '\n❗❗❗❗❗❗\n成功开出 永久雷诺\n❗❗❗❗❗❗\n'
150 | print('\n❗❗❗❗❗❗\n成功开出 永久雷诺\n❗❗❗❗❗❗\n')
151 | break
152 | if '不足' in msg:
153 | break
154 |
155 | i += 1
156 |
157 | if sendnoty:
158 | try:
159 | send('掌上飞车开金丝篓', msg)
160 | except Exception as err:
161 | print('%s\n错误,请查看运行日志!' % err)
162 | send('掌上飞车开金丝篓', '%s\n错误,请查看运行日志!' % err)
163 |
164 | return msg[:-1]
165 |
166 |
167 | if __name__ == "__main__":
168 | print("----------掌上飞车开始尝试开金丝篓----------")
169 | main()
170 | print("----------掌上飞车开金丝篓执行完毕----------")
--------------------------------------------------------------------------------
/QQSpeed/sign.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | """
4 | new Env('掌上飞车签到')
5 | cron: 0 0 0 * * *
6 | Author : SmallBaby
7 | Date : 2023-01-09 09:00:00
8 | LastEditTime : 2023-01-10 13:00:00
9 | FilePath : QingLongScript/QQSpeed/sign.py
10 | Description :
11 | 添加环境变量QQ_SPEED_COOKIE、QQ_SPEED_REFERER,多账号用回车换行分开
12 | 值分别是cookie和referer
13 | """
14 | import threading
15 | from datetime import datetime
16 | from bs4 import BeautifulSoup
17 | import requests
18 | from os import environ
19 | import sys
20 | import re
21 | import time
22 |
23 | sys.path.append('../')
24 |
25 | try:
26 | import Utils.notify
27 | except Exception as err: # 异常捕捉
28 | print(f'{err}\n加载通知服务失败~\n')
29 |
30 | # 测试用环境变量
31 | # environ['QQ_SPEED_COOKIE'] = ''
32 | # environ['QQ_SPEED_REFERER'] = ''
33 |
34 |
35 | class Sign:
36 | def __init__(self, cookie, referer):
37 | self.cookie = cookie
38 | self.referer = f'{baseHost}?{referer.split("?")[1]}'
39 | self.headers = get_headers(self.cookie, self.referer)
40 | self.signInfo, self.benefitsArr, self.signParam = self.get_sign_index()
41 | self.msg = f'账号{self.signParam.get("params").get("roleId")}签到结果:\n'
42 |
43 | # 获取签到页面
44 | def get_sign_index(self):
45 | headers = self.headers
46 | res = requests.get(self.referer, headers=headers)
47 | htmlStr = res.text
48 | html = BeautifulSoup(htmlStr, 'html.parser')
49 | tabSoupArr = html.select('.evt-tab-panel')
50 | signSoupArr = tabSoupArr[0]
51 | benefitsSoupArr = tabSoupArr[1]
52 | signInfo = {}
53 | benefitsArr = []
54 |
55 | # 获取每日签到福利signInfo
56 | for target in signSoupArr.find_all('dl'):
57 | key = target.select_one('span').text
58 | signInfo[key] = {
59 | 'giftId': target.select_one('a').get('giftid'),
60 | 'status': target.select_one('a').get('class')[0] == 'received',
61 | 'label': target.select_one('.name').text
62 | }
63 | signInfo['user'] = {
64 | 'giftId': signSoupArr.select_one('#signButton').get('giftid'),
65 | 'status': signSoupArr.select_one('#signButton').get('class')[0] == 'btn_signed',
66 | 'label': html.select_one('.cumulative').text,
67 | 'count': int(html.select_one('#my_count').text)
68 | }
69 |
70 | # 获取特殊福利benefitsArr
71 | for target in benefitsSoupArr.select('.gift-bag'):
72 | benefits = {
73 | 'giftId': target.select_one('a').get('giftid'),
74 | 'status': target.select_one('a').get('class')[0] == 'received',
75 | 'datetime': target.select_one('.text2').text,
76 | 'label': f'{target.select_one(".text2").text}: {target.select_one(".name").text}'
77 | }
78 | benefitsArr.append(benefits)
79 |
80 | # 获取signParam
81 | pattern = re.compile(r'var signParam = (\{.*}.*?$).*?var shareInfo', re.MULTILINE | re.DOTALL)
82 | evalScript = html.find('script', text=pattern)
83 | signParamStr = re.findall(pattern, evalScript.text)[0]
84 | signParam = eval(signParamStr, type('js', (dict,), dict(__getitem__=lambda s, n: n))())
85 |
86 | return signInfo, benefitsArr, signParam
87 |
88 | # 开始签到
89 | def do_sign(self):
90 | headers = self.headers
91 | url = f'https://mwegame.qq.com/ams/sign/doSign/{self.signParam.get("type")}'
92 | params = {
93 | **self.signParam.get('params'),
94 | 'gift_id': self.signInfo.get('user').get('giftId')
95 | }
96 | res = requests.get(url, headers=headers, params=params).json()
97 | msg = f'{res.get("message", "")}: {res.get("send_result", {}).get("sMsg", "")}\n'
98 | print(msg)
99 | self.msg += msg
100 | return res.get('status') == 1
101 |
102 | # 领取福利
103 | def get_gift(self, gift_id):
104 | headers = self.headers
105 | url = 'https://mwegame.qq.com/ams/send/handle'
106 | data = {
107 | **self.signParam.get('params'),
108 | 'gift_id': gift_id
109 | }
110 | res = requests.post(url, headers=headers, data=data).json()
111 | msg = f'{res.get("message", "")}{res.get("data", "")}: {res.get("send_result", {}).get("sMsg", "")}\n'
112 | print(msg)
113 | self.msg += msg
114 | return res.get('status') == 1
115 |
116 | # 鉴权汇报
117 | def login_analysis(self, eid=10011):
118 | headers = self.headers
119 | url = 'https://mwegame.qq.com/login/analysis'
120 | data = {
121 | 'mid': 10,
122 | 'eid': eid,
123 | 'surl': '',
124 | 'durl': self.referer,
125 | 'qq': self.signParam.get('params').get('roleId'),
126 | 'openid': self.signParam.get('params').get('appOpenid'),
127 | 'game': 'speed',
128 | 'bd': 0,
129 | 'qid': 3,
130 | 'aid': self.signParam.get('params').get('userId'),
131 | 'from': 'mobile',
132 | 'ref': '',
133 | 'encodeOpenId': 0,
134 | 'encodeQQ': 0,
135 | }
136 | res = requests.post(url, headers=headers, data=data).json()
137 | return res.get('res') == 0
138 |
139 | def main(self):
140 | userId = self.signParam.get('params').get('roleId')
141 | print(f'线程-{userId} 创建成功\n')
142 | userSign = self.signInfo.get('user')
143 | benefits = self.benefitsArr[0]
144 | isLogin = self.login_analysis()
145 | if isLogin:
146 | # 每日签到
147 | if userSign.get('status'):
148 | print(userSign.get('label'))
149 | self.msg += f'{userSign.get("label")}'
150 | else:
151 | if self.do_sign():
152 | userSign['count'] += 1
153 | print(f'本月累计签到 {userSign["count"]} 天\n')
154 | self.msg += f'本月累计签到 {userSign["count"]} 天\n'
155 | # 每日签到福利领取
156 | for key in self.signInfo.keys():
157 | if key == 'user':
158 | continue
159 | if int(key) > userSign.get('count'):
160 | break
161 | if not self.signInfo[key].get('status'):
162 | self.get_gift(self.signInfo[key].get('giftId'))
163 | # 延迟2秒执行,防止频繁
164 | time.sleep(2)
165 | # 特殊福利领取
166 | if benefits.get('datetime') <= datetime.now().strftime('%m月%d日'):
167 | self.get_gift(benefits.get('giftId'))
168 | try:
169 | notify.send('掌上飞车签到', self.msg)
170 | except Exception as err: # 异常捕捉
171 | print(f'{err}\n加载通知服务失败~\n')
172 |
173 |
174 | # 构造请求头
175 | def get_headers(cookie='', referer=''):
176 | headers = {
177 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; Redmi K20 Pro Premium Edition Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.105 Mobile Safari/537.36 GH_QQConnect GameHelper_1003/2103040778',
178 | 'Connection': 'keep-alive',
179 | 'Accept': 'application/json',
180 | 'Accept-Encoding': 'gzip, deflate, br',
181 | 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
182 | }
183 | if cookie != '':
184 | headers['Cookie'] = cookie
185 | if referer != '':
186 | if '?' in referer:
187 | headers['Referer'] = f'{baseHost}?{referer.split("?")[1]}'
188 | else:
189 | headers['Referer'] = referer
190 | return headers
191 |
192 |
193 | # 读取环境变量并格式化
194 | def get_cookie():
195 | cookie_arr = get_environ('QQ_SPEED_COOKIE').split('\n')
196 | referer_arr = get_environ('QQ_SPEED_REFERER').split('\n')
197 | return cookie_arr, referer_arr
198 |
199 |
200 | # 封装读取环境变量的方法
201 | def get_environ(key, default=''):
202 | if len(environ.get(key, '')) == 0:
203 | print(f'环境变量 {key} 未添加,程序已退出\n')
204 | exit()
205 | return environ.get(key, default)
206 |
207 |
208 | def run_threading(cookie, referer):
209 | Sign(cookie, referer).main()
210 |
211 |
212 | if __name__ == '__main__':
213 | baseHost = 'https://mwegame.qq.com/ams/sign/month/speed'
214 | cookieArr, refererArr = get_cookie()
215 | threadArr = []
216 | for i in range(len(cookieArr)):
217 | if len(refererArr) < i + 1:
218 | print('填写了Cookie,但没填写Referer,跳过该账号\n')
219 | continue
220 | threadUser = threading.Thread(target=run_threading, args=(cookieArr[i], refererArr[i]))
221 | threadUser.start()
222 | threadArr.append(threadUser)
223 | for i in range(len(cookieArr)):
224 | threadArr[i].join()
225 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QingLongScript
2 |
3 | ## 青龙脚本
4 |
5 | > AppShare | Bilibili | NOC指导教师认证 | 中国电信 | 中国联通 | 小米社区 | 掌上飞车
6 |
7 | - 随缘维护和上传, 请不要上传或者转载到其它地方。
8 | - 本仓库脚本仅供用于学习及参考对应web/app的参数解密并提供对应demo用于测试,请不要询问如何多账号等
9 | - 食用方法都在脚本内,请自行查看。
10 | - 觉得好用可以点个star。
11 |
12 | ## 环境变量说明
13 |
14 | - 待补充
15 |
16 | ## 文件目录说明
17 |
18 | - 主目录 -- 存放主文件
19 | - Utils -- 存放一些脚本内经常需要重复使用的工具
20 |
21 | ## 特别声明
22 |
23 | - 本仓库发布的脚本及其中涉及的任何解密分析脚本,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。
24 | - 本项目内所有资源文件,禁止任何公众号、自媒体进行任何形式的转载、发布。
25 | - 本人对任何脚本问题概不负责,包括但不限于由任何脚本错误导致的任何损失或损害。
26 | - 间接使用脚本的任何用户,包括但不限于建立VPS或在某些行为违反国家/地区法律或相关法规的情况下进行传播, 本人对于由此引起的任何隐私泄漏或其他后果概不负责。
27 | - 请勿将本仓库的任何内容用于商业或非法目的,否则后果自负。
28 | - 如果任何单位或个人认为该项目的脚本可能涉嫌侵犯其权利,则应及时通知并提供身份证明,所有权证明,我们将在收到认证文件后删除相关脚本。
29 | - 任何以任何方式查看此项目的人或直接或间接使用该项目的任何脚本的使用者都应仔细阅读此声明。本人保留随时更改或补充此免责声明的权利。一旦使用并复制了任何相关脚本或本项目的规则,则视为您已接受此免责声明。
30 |
31 | **您必须在下载后的24小时内从计算机或手机中完全删除以上内容**
32 |
--------------------------------------------------------------------------------
/Utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l526614169/QingLongScript/1e21062eae3a97f2d672d905bc05f9943fec4548/Utils/__init__.py
--------------------------------------------------------------------------------
/Utils/aes_encrypt.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | # -------------------------------
4 | # @Author : github@limoruirui https://github.com/limoruirui
5 | # @Time : 2022/8/22 18:13
6 | # -------------------------------
7 | """
8 | aes加密解密工具 目前仅支持ECB/CBC 块长度均为128位 padding只支持pkcs7/zero_padding(aes中没有pkcs5 能用的pkcs5其实是执行的pkcs7) 后续有需要再加
9 | pycryptdemo限制 同一个aes加密对象不能即加密又解密 所以当加密和解密都需要执行时 需要重新new一个对象增加额外开销
10 | -- A cipher object is stateful: once you have encrypted a message , you cannot encrypt (or decrypt) another message using the same object.
11 | """
12 | from Crypto.Cipher import AES
13 | from binascii import b2a_hex, a2b_hex
14 | from base64 import b64encode, b64decode
15 | class AES_Ctypt:
16 | def __init__(self, key, iv=None, mode="ECB"):
17 | if len(key) % 16 != 0:
18 | key = key + (AES.block_size - len(key) % AES.block_size) * chr(0)
19 | self.key = key.encode("utf-8")
20 | if mode == "ECB":
21 | self.mode = AES.MODE_ECB
22 | elif mode == "CBC":
23 | self.mode = AES.MODE_CBC
24 | else:
25 | print("您选择的加密方式错误")
26 | if iv is None:
27 | self.cipher = AES.new(self.key, self.mode)
28 | else:
29 | if isinstance(iv, str):
30 | self.cipher = AES.new(self.key, self.mode, iv.encode("utf-8"))
31 | else:
32 | print("偏移量不为字符串")
33 | def encrypt(self, data, padding="pkcs7", b64=False):
34 | bs = AES.block_size
35 | pkcs7_padding = lambda s: s + (bs - len(s.encode()) % bs) * chr(bs - len(s.encode()) % bs)
36 | zero_padding = lambda s: s + (bs - len(s) % bs) * chr(0)
37 | pad = pkcs7_padding if padding=="pkcs7" else zero_padding
38 | data = self.cipher.encrypt(pad(data).encode("utf8"))
39 | encrypt_data = b64encode(data) if b64 else b2a_hex(data) # 输出hex或者base64
40 | return encrypt_data.decode('utf8')
41 | def decrypt(self, data, b64=False):
42 | data = b64decode(data) if b64 else a2b_hex(data)
43 | decrypt_data = self.cipher.decrypt(data).decode()
44 | # 去掉padding
45 | # pkcs7_unpadding = lambda s: s.replace(s[-1], "")
46 | # zero_unpadding = lambda s: s.replace(chr(0), "")
47 | # unpadding = pkcs7_unpadding if padding=="pkcs7" else zero_unpadding
48 | unpadding = lambda s: s.replace(s[-1], "")
49 | return unpadding(decrypt_data)
--------------------------------------------------------------------------------
/Utils/encrypt_symmetric.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | # -------------------------------
4 | # @Author : github@limoruirui https://github.com/limoruirui
5 | # @Time : 2022/10/24 22:09
6 | # -------------------------------
7 | # !/usr/bin/python3
8 | # -- coding: utf-8 --
9 | # -------------------------------
10 | # @Author : github@limoruirui https://github.com/limoruirui
11 | # @Time : 2022/8/22 18:13
12 | # -------------------------------
13 | """
14 | aes加密解密工具 目前仅支持ECB/CBC 块长度均为128位 padding只支持pkcs7/zero_padding(aes中没有pkcs5 能用的pkcs5其实是执行的pkcs7) 后续有需要再加
15 | pycryptdemo限制 同一个aes加密对象不能即加密又解密 所以当加密和解密都需要执行时 需要重新new一个对象增加额外开销
16 | -- A cipher object is stateful: once you have encrypted a message , you cannot encrypt (or decrypt) another message using the same object.
17 | """
18 | from Crypto.Cipher import AES, DES, DES3
19 | from binascii import b2a_hex, a2b_hex
20 | from base64 import b64encode, b64decode
21 |
22 |
23 | class Crypt:
24 | def __init__(self, crypt_type: str, key, iv=None, mode="ECB"):
25 | """
26 |
27 | :param crypt_type: 对称加密类型 支持AES, DES, DES3
28 | :param key: 密钥 (aes可选 16/32(24位暂不支持 以后遇到有需要再补) des 固定为8 des3 24(暂不支持16 16应该也不会再使用了) 一般都为24 分为8长度的三组 进行三次des加密
29 | :param iv: 偏移量
30 | :param mode: 模式 CBC/ECB
31 | """
32 | if crypt_type.upper() not in ["AES", "DES", "DES3"]:
33 | raise Exception("加密类型错误, 请重新选择 AES/DES/DES3")
34 | self.crypt_type = AES if crypt_type.upper() == "AES" else DES if crypt_type.upper() == "DES" else DES3
35 | self.block_size = self.crypt_type.block_size
36 | if self.crypt_type == DES:
37 | self.key_size = self.crypt_type.key_size
38 | elif self.crypt_type == DES3:
39 | self.key_size = self.crypt_type.key_size[1]
40 | else:
41 | if len(key) <= 16:
42 | self.key_size = self.crypt_type.key_size[0]
43 | elif len(key) > 24:
44 | self.key_size = self.crypt_type.key_size[2]
45 | else:
46 | self.key_size = self.crypt_type.key_size[1]
47 | print("当前aes密钥的长度只填充到24 若需要32 请手动用 chr(0) 填充")
48 | if len(key) > self.key_size:
49 | key = key[:self.key_size]
50 | else:
51 | if len(key) % self.key_size != 0:
52 | key = key + (self.key_size - len(key) % self.key_size) * chr(0)
53 | self.key = key.encode("utf-8")
54 | if mode == "ECB":
55 | self.mode = self.crypt_type.MODE_ECB
56 | elif mode == "CBC":
57 | self.mode = self.crypt_type.MODE_CBC
58 | else:
59 | raise Exception("您选择的加密模式错误")
60 | if iv is None:
61 | self.cipher = self.crypt_type.new(self.key, self.mode)
62 | else:
63 | if isinstance(iv, str):
64 | iv = iv[:self.block_size]
65 | self.cipher = self.crypt_type.new(self.key, self.mode, iv.encode("utf-8"))
66 | elif isinstance(iv, bytes):
67 | iv = iv[:self.block_size]
68 | self.cipher = self.crypt_type.new(self.key, self.mode, iv)
69 | else:
70 | raise Exception("偏移量不为字符串")
71 |
72 | def encrypt(self, data, padding="pkcs7", b64=False):
73 | """
74 |
75 | :param data: 目前暂不支持bytes 只支持string 有需求再补
76 | :param padding: pkcs7/pkck5 zero
77 | :param b64: 若需要得到base64的密文 则为True
78 | :return:
79 | """
80 | pkcs7_padding = lambda s: s + (self.block_size - len(s.encode()) % self.block_size) * chr(
81 | self.block_size - len(s.encode()) % self.block_size)
82 | zero_padding = lambda s: s + (self.block_size - len(s) % self.block_size) * chr(0)
83 | pad = pkcs7_padding if padding == "pkcs7" else zero_padding
84 | data = self.cipher.encrypt(pad(data).encode("utf8"))
85 | encrypt_data = b64encode(data) if b64 else b2a_hex(data) # 输出hex或者base64
86 | return encrypt_data.decode('utf8')
87 |
88 | def decrypt(self, data, b64=False):
89 | """
90 | 对称加密的解密
91 | :param data: 支持bytes base64 hex list 未做填充 密文应该都是数据块的倍数 带有需求再补
92 | :param b64: 若传入的data为base64格式 则为True
93 | :return:
94 | """
95 | if isinstance(data, list):
96 | data = bytes(data)
97 | if not isinstance(data, bytes):
98 | data = b64decode(data) if b64 else a2b_hex(data)
99 | decrypt_data = self.cipher.decrypt(data).decode()
100 | # 去掉padding
101 | # pkcs7_unpadding = lambda s: s.replace(s[-1], "")
102 | # zero_unpadding = lambda s: s.replace(chr(0), "")
103 | # unpadding = pkcs7_unpadding if padding=="pkcs7" else zero_unpadding
104 | unpadding = lambda s: s.replace(s[-1], "")
105 | return unpadding(decrypt_data)
106 |
--------------------------------------------------------------------------------
/Utils/notify.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # _*_ coding:utf-8 _*_
3 | import base64
4 | import hashlib
5 | import hmac
6 | import json
7 | import os
8 | import re
9 | import threading
10 | import time
11 | import urllib.parse
12 |
13 | import requests
14 |
15 | # 原先的 print 函数和主线程的锁
16 | _print = print
17 | mutex = threading.Lock()
18 |
19 |
20 | # 定义新的 print 函数
21 | def print(text, *args, **kw):
22 | """
23 | 使输出有序进行,不出现多线程同一时间输出导致错乱的问题。
24 | """
25 | with mutex:
26 | _print(text, *args, **kw)
27 |
28 |
29 | # 通知服务
30 | # fmt: off
31 | push_config = {
32 | 'HITOKOTO': False, # 启用一言(随机句子)
33 |
34 | 'BARK_PUSH': '', # bark IP 或设备码,例:https://api.day.app/DxHcxxxxxRxxxxxxcm/
35 | 'BARK_ARCHIVE': '', # bark 推送是否存档
36 | 'BARK_GROUP': '', # bark 推送分组
37 | 'BARK_SOUND': '', # bark 推送声音
38 | 'BARK_ICON': '', # bark 推送图标
39 |
40 | 'CONSOLE': True, # 控制台输出
41 |
42 | 'DD_BOT_SECRET': '', # 钉钉机器人的 DD_BOT_SECRET
43 | 'DD_BOT_TOKEN': '', # 钉钉机器人的 DD_BOT_TOKEN
44 |
45 | 'FSKEY': '', # 飞书机器人的 FSKEY
46 |
47 | 'GOBOT_URL': '', # go-cqhttp
48 | # 推送到个人QQ:http://127.0.0.1/send_private_msg
49 | # 群:http://127.0.0.1/send_group_msg
50 | 'GOBOT_QQ': '', # go-cqhttp 的推送群或用户
51 | # GOBOT_URL 设置 /send_private_msg 时填入 user_id=个人QQ
52 | # /send_group_msg 时填入 group_id=QQ群
53 | 'GOBOT_TOKEN': '', # go-cqhttp 的 access_token
54 |
55 | 'GOTIFY_URL': '', # gotify地址,如https://push.example.de:8080
56 | 'GOTIFY_TOKEN': '', # gotify的消息应用token
57 | 'GOTIFY_PRIORITY': 0, # 推送消息优先级,默认为0
58 |
59 | 'IGOT_PUSH_KEY': '', # iGot 聚合推送的 IGOT_PUSH_KEY
60 |
61 | 'PUSH_KEY': '', # server 酱的 PUSH_KEY,兼容旧版与 Turbo 版
62 |
63 | 'DEER_KEY': '', # PushDeer 的 PUSHDEER_KEY
64 | 'DEER_URL': '', # PushDeer 的 PUSHDEER_URL
65 |
66 | 'CHAT_URL': '', # synology chat url
67 | 'CHAT_TOKEN': '', # synology chat token
68 |
69 | 'PUSH_PLUS_TOKEN': '', # push+ 微信推送的用户令牌
70 | 'PUSH_PLUS_USER': '', # push+ 微信推送的群组编码
71 |
72 | 'QMSG_KEY': '', # qmsg 酱的 QMSG_KEY
73 | 'QMSG_TYPE': '', # qmsg 酱的 QMSG_TYPE
74 |
75 | 'QYWX_AM': '', # 企业微信应用
76 |
77 | 'QYWX_KEY': '', # 企业微信机器人
78 |
79 | 'TG_BOT_TOKEN': '', # tg 机器人的 TG_BOT_TOKEN,例:1407203283:AAG9rt-6RDaaX0HBLZQq0laNOh898iFYaRQ
80 | 'TG_USER_ID': '', # tg 机器人的 TG_USER_ID,例:1434078534
81 | 'TG_API_HOST': '', # tg 代理 api
82 | 'TG_PROXY_AUTH': '', # tg 代理认证参数
83 | 'TG_PROXY_HOST': '', # tg 机器人的 TG_PROXY_HOST
84 | 'TG_PROXY_PORT': '', # tg 机器人的 TG_PROXY_PORT
85 |
86 | 'AIBOTK_KEY': '', # 智能微秘书 个人中心的apikey 文档地址:http://wechat.aibotk.com/docs/about
87 | 'AIBOTK_TYPE': '', # 智能微秘书 发送目标 room 或 contact
88 | 'AIBOTK_NAME': '', # 智能微秘书 发送群名 或者好友昵称和type要对应好
89 | }
90 | notify_function = []
91 | # fmt: on
92 |
93 | # 首先读取 面板变量 或者 github action 运行变量
94 | for k in push_config:
95 | if os.getenv(k):
96 | v = os.getenv(k)
97 | push_config[k] = v
98 |
99 |
100 | def bark(title: str, content: str) -> None:
101 | """
102 | 使用 bark 推送消息。
103 | """
104 | if not push_config.get("BARK_PUSH"):
105 | print("bark 服务的 BARK_PUSH 未设置!!\n取消推送")
106 | return
107 | print("bark 服务启动")
108 |
109 | if push_config.get("BARK_PUSH").startswith("http"):
110 | url = f'{push_config.get("BARK_PUSH")}/{urllib.parse.quote_plus(title)}/{urllib.parse.quote_plus(content)}'
111 | else:
112 | url = f'https://api.day.app/{push_config.get("BARK_PUSH")}/{urllib.parse.quote_plus(title)}/{urllib.parse.quote_plus(content)}'
113 |
114 | bark_params = {
115 | "BARK_ARCHIVE": "isArchive",
116 | "BARK_GROUP": "group",
117 | "BARK_SOUND": "sound",
118 | "BARK_ICON": "icon",
119 | }
120 | params = ""
121 | for pair in filter(
122 | lambda pairs: pairs[0].startswith("BARK_")
123 | and pairs[0] != "BARK_PUSH"
124 | and pairs[1]
125 | and bark_params.get(pairs[0]),
126 | push_config.items(),
127 | ):
128 | params += f"{bark_params.get(pair[0])}={pair[1]}&"
129 | if params:
130 | url = url + "?" + params.rstrip("&")
131 | response = requests.get(url).json()
132 |
133 | if response["code"] == 200:
134 | print("bark 推送成功!")
135 | else:
136 | print("bark 推送失败!")
137 |
138 |
139 | def console(title: str, content: str) -> None:
140 | """
141 | 使用 控制台 推送消息。
142 | """
143 | print(f"{title}\n\n{content}")
144 |
145 |
146 | def dingding_bot(title: str, content: str) -> None:
147 | """
148 | 使用 钉钉机器人 推送消息。
149 | """
150 | if not push_config.get("DD_BOT_SECRET") or not push_config.get("DD_BOT_TOKEN"):
151 | print("钉钉机器人 服务的 DD_BOT_SECRET 或者 DD_BOT_TOKEN 未设置!!\n取消推送")
152 | return
153 | print("钉钉机器人 服务启动")
154 |
155 | timestamp = str(round(time.time() * 1000))
156 | secret_enc = push_config.get("DD_BOT_SECRET").encode("utf-8")
157 | string_to_sign = "{}\n{}".format(timestamp, push_config.get("DD_BOT_SECRET"))
158 | string_to_sign_enc = string_to_sign.encode("utf-8")
159 | hmac_code = hmac.new(
160 | secret_enc, string_to_sign_enc, digestmod=hashlib.sha256
161 | ).digest()
162 | sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
163 | url = f'https://oapi.dingtalk.com/robot/send?access_token={push_config.get("DD_BOT_TOKEN")}×tamp={timestamp}&sign={sign}'
164 | headers = {"Content-Type": "application/json;charset=utf-8"}
165 | data = {"msgtype": "text", "text": {"content": f"{title}\n\n{content}"}}
166 | response = requests.post(
167 | url=url, data=json.dumps(data), headers=headers, timeout=15
168 | ).json()
169 |
170 | if not response["errcode"]:
171 | print("钉钉机器人 推送成功!")
172 | else:
173 | print("钉钉机器人 推送失败!")
174 |
175 |
176 | def feishu_bot(title: str, content: str) -> None:
177 | """
178 | 使用 飞书机器人 推送消息。
179 | """
180 | if not push_config.get("FSKEY"):
181 | print("飞书 服务的 FSKEY 未设置!!\n取消推送")
182 | return
183 | print("飞书 服务启动")
184 |
185 | url = f'https://open.feishu.cn/open-apis/bot/v2/hook/{push_config.get("FSKEY")}'
186 | data = {"msg_type": "text", "content": {"text": f"{title}\n\n{content}"}}
187 | response = requests.post(url, data=json.dumps(data)).json()
188 |
189 | if response.get("StatusCode") == 0:
190 | print("飞书 推送成功!")
191 | else:
192 | print("飞书 推送失败!错误信息如下:\n", response)
193 |
194 |
195 | def go_cqhttp(title: str, content: str) -> None:
196 | """
197 | 使用 go_cqhttp 推送消息。
198 | """
199 | if not push_config.get("GOBOT_URL") or not push_config.get("GOBOT_QQ"):
200 | print("go-cqhttp 服务的 GOBOT_URL 或 GOBOT_QQ 未设置!!\n取消推送")
201 | return
202 | print("go-cqhttp 服务启动")
203 |
204 | url = f'{push_config.get("GOBOT_URL")}?access_token={push_config.get("GOBOT_TOKEN")}&{push_config.get("GOBOT_QQ")}&message=标题:{title}\n内容:{content}'
205 | response = requests.get(url).json()
206 |
207 | if response["status"] == "ok":
208 | print("go-cqhttp 推送成功!")
209 | else:
210 | print("go-cqhttp 推送失败!")
211 |
212 |
213 | def gotify(title: str, content: str) -> None:
214 | """
215 | 使用 gotify 推送消息。
216 | """
217 | if not push_config.get("GOTIFY_URL") or not push_config.get("GOTIFY_TOKEN"):
218 | print("gotify 服务的 GOTIFY_URL 或 GOTIFY_TOKEN 未设置!!\n取消推送")
219 | return
220 | print("gotify 服务启动")
221 |
222 | url = f'{push_config.get("GOTIFY_URL")}/message?token={push_config.get("GOTIFY_TOKEN")}'
223 | data = {"title": title, "message": content, "priority": push_config.get("GOTIFY_PRIORITY")}
224 | response = requests.post(url, data=data).json()
225 |
226 | if response.get("id"):
227 | print("gotify 推送成功!")
228 | else:
229 | print("gotify 推送失败!")
230 |
231 |
232 | def iGot(title: str, content: str) -> None:
233 | """
234 | 使用 iGot 推送消息。
235 | """
236 | if not push_config.get("IGOT_PUSH_KEY"):
237 | print("iGot 服务的 IGOT_PUSH_KEY 未设置!!\n取消推送")
238 | return
239 | print("iGot 服务启动")
240 |
241 | url = f'https://push.hellyw.com/{push_config.get("IGOT_PUSH_KEY")}'
242 | data = {"title": title, "content": content}
243 | headers = {"Content-Type": "application/x-www-form-urlencoded"}
244 | response = requests.post(url, data=data, headers=headers).json()
245 |
246 | if response["ret"] == 0:
247 | print("iGot 推送成功!")
248 | else:
249 | print(f'iGot 推送失败!{response["errMsg"]}')
250 |
251 |
252 | def serverJ(title: str, content: str) -> None:
253 | """
254 | 通过 serverJ 推送消息。
255 | """
256 | if not push_config.get("PUSH_KEY"):
257 | print("serverJ 服务的 PUSH_KEY 未设置!!\n取消推送")
258 | return
259 | print("serverJ 服务启动")
260 |
261 | data = {"text": title, "desp": content.replace("\n", "\n\n")}
262 | if push_config.get("PUSH_KEY").index("SCT") != -1:
263 | url = f'https://sctapi.ftqq.com/{push_config.get("PUSH_KEY")}.send'
264 | else:
265 | url = f'https://sc.ftqq.com/${push_config.get("PUSH_KEY")}.send'
266 | response = requests.post(url, data=data).json()
267 |
268 | if response.get("errno") == 0 or response.get("code") == 0:
269 | print("serverJ 推送成功!")
270 | else:
271 | print(f'serverJ 推送失败!错误码:{response["message"]}')
272 |
273 |
274 | def pushdeer(title: str, content: str) -> None:
275 | """
276 | 通过PushDeer 推送消息
277 | """
278 | if not push_config.get("DEER_KEY"):
279 | print("PushDeer 服务的 DEER_KEY 未设置!!\n取消推送")
280 | return
281 | print("PushDeer 服务启动")
282 | data = {"text": title, "desp": content, "type": "markdown", "pushkey": push_config.get("DEER_KEY")}
283 | url = 'https://api2.pushdeer.com/message/push'
284 | if push_config.get("DEER_URL"):
285 | url = push_config.get("DEER_URL")
286 |
287 | response = requests.post(url, data=data).json()
288 |
289 | if len(response.get("content").get("result")) > 0:
290 | print("PushDeer 推送成功!")
291 | else:
292 | print("PushDeer 推送失败!错误信息:", response)
293 |
294 |
295 | def chat(title: str, content: str) -> None:
296 | """
297 | 通过Chat 推送消息
298 | """
299 | if not push_config.get("CHAT_URL") or not push_config.get("CHAT_TOKEN"):
300 | print("chat 服务的 CHAT_URL或CHAT_TOKEN 未设置!!\n取消推送")
301 | return
302 | print("chat 服务启动")
303 | data = 'payload=' + json.dumps({'text': title + '\n' + content})
304 | url = push_config.get("CHAT_URL") + push_config.get("CHAT_TOKEN")
305 | response = requests.post(url, data=data)
306 |
307 | if response.status_code == 200:
308 | print("Chat 推送成功!")
309 | else:
310 | print("Chat 推送失败!错误信息:", response)
311 |
312 |
313 | def pushplus_bot(title: str, content: str) -> None:
314 | """
315 | 通过 push+ 推送消息。
316 | """
317 | if not push_config.get("PUSH_PLUS_TOKEN"):
318 | print("PUSHPLUS 服务的 PUSH_PLUS_TOKEN 未设置!!\n取消推送")
319 | return
320 | print("PUSHPLUS 服务启动")
321 |
322 | url = "http://www.pushplus.plus/send"
323 | data = {
324 | "token": push_config.get("PUSH_PLUS_TOKEN"),
325 | "title": title,
326 | "content": content,
327 | "topic": push_config.get("PUSH_PLUS_USER"),
328 | }
329 | body = json.dumps(data).encode(encoding="utf-8")
330 | headers = {"Content-Type": "application/json"}
331 | response = requests.post(url=url, data=body, headers=headers).json()
332 |
333 | if response["code"] == 200:
334 | print("PUSHPLUS 推送成功!")
335 |
336 | else:
337 |
338 | url_old = "http://pushplus.hxtrip.com/send"
339 | headers["Accept"] = "application/json"
340 | response = requests.post(url=url_old, data=body, headers=headers).json()
341 |
342 | if response["code"] == 200:
343 | print("PUSHPLUS(hxtrip) 推送成功!")
344 |
345 | else:
346 | print("PUSHPLUS 推送失败!")
347 |
348 |
349 | def qmsg_bot(title: str, content: str) -> None:
350 | """
351 | 使用 qmsg 推送消息。
352 | """
353 | if not push_config.get("QMSG_KEY") or not push_config.get("QMSG_TYPE"):
354 | print("qmsg 的 QMSG_KEY 或者 QMSG_TYPE 未设置!!\n取消推送")
355 | return
356 | print("qmsg 服务启动")
357 |
358 | url = f'https://qmsg.zendee.cn/{push_config.get("QMSG_TYPE")}/{push_config.get("QMSG_KEY")}'
359 | payload = {"msg": f'{title}\n\n{content.replace("----", "-")}'.encode("utf-8")}
360 | response = requests.post(url=url, params=payload).json()
361 |
362 | if response["code"] == 0:
363 | print("qmsg 推送成功!")
364 | else:
365 | print(f'qmsg 推送失败!{response["reason"]}')
366 |
367 |
368 | def wecom_app(title: str, content: str) -> None:
369 | """
370 | 通过 企业微信 APP 推送消息。
371 | """
372 | if not push_config.get("QYWX_AM"):
373 | print("QYWX_AM 未设置!!\n取消推送")
374 | return
375 | QYWX_AM_AY = re.split(",", push_config.get("QYWX_AM"))
376 | if 4 < len(QYWX_AM_AY) > 5:
377 | print("QYWX_AM 设置错误!!\n取消推送")
378 | return
379 | print("企业微信 APP 服务启动")
380 |
381 | corpid = QYWX_AM_AY[0]
382 | corpsecret = QYWX_AM_AY[1]
383 | touser = QYWX_AM_AY[2]
384 | agentid = QYWX_AM_AY[3]
385 | try:
386 | media_id = QYWX_AM_AY[4]
387 | except IndexError:
388 | media_id = ""
389 | wx = WeCom(corpid, corpsecret, agentid)
390 | # 如果没有配置 media_id 默认就以 text 方式发送
391 | if not media_id:
392 | message = title + "\n\n" + content
393 | response = wx.send_text(message, touser)
394 | else:
395 | response = wx.send_mpnews(title, content, media_id, touser)
396 |
397 | if response == "ok":
398 | print("企业微信推送成功!")
399 | else:
400 | print("企业微信推送失败!错误信息如下:\n", response)
401 |
402 |
403 | class WeCom:
404 | def __init__(self, corpid, corpsecret, agentid):
405 | self.CORPID = corpid
406 | self.CORPSECRET = corpsecret
407 | self.AGENTID = agentid
408 |
409 | def get_access_token(self):
410 | url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
411 | values = {
412 | "corpid": self.CORPID,
413 | "corpsecret": self.CORPSECRET,
414 | }
415 | req = requests.post(url, params=values)
416 | data = json.loads(req.text)
417 | return data["access_token"]
418 |
419 | def send_text(self, message, touser="@all"):
420 | send_url = (
421 | "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token="
422 | + self.get_access_token()
423 | )
424 | send_values = {
425 | "touser": touser,
426 | "msgtype": "text",
427 | "agentid": self.AGENTID,
428 | "text": {"content": message},
429 | "safe": "0",
430 | }
431 | send_msges = bytes(json.dumps(send_values), "utf-8")
432 | respone = requests.post(send_url, send_msges)
433 | respone = respone.json()
434 | return respone["errmsg"]
435 |
436 | def send_mpnews(self, title, message, media_id, touser="@all"):
437 | send_url = (
438 | "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token="
439 | + self.get_access_token()
440 | )
441 | send_values = {
442 | "touser": touser,
443 | "msgtype": "mpnews",
444 | "agentid": self.AGENTID,
445 | "mpnews": {
446 | "articles": [
447 | {
448 | "title": title,
449 | "thumb_media_id": media_id,
450 | "author": "Author",
451 | "content_source_url": "",
452 | "content": message.replace("\n", "
"),
453 | "digest": message,
454 | }
455 | ]
456 | },
457 | }
458 | send_msges = bytes(json.dumps(send_values), "utf-8")
459 | respone = requests.post(send_url, send_msges)
460 | respone = respone.json()
461 | return respone["errmsg"]
462 |
463 |
464 | def wecom_bot(title: str, content: str) -> None:
465 | """
466 | 通过 企业微信机器人 推送消息。
467 | """
468 | if not push_config.get("QYWX_KEY"):
469 | print("企业微信机器人 服务的 QYWX_KEY 未设置!!\n取消推送")
470 | return
471 | print("企业微信机器人服务启动")
472 |
473 | url = f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={push_config.get('QYWX_KEY')}"
474 | headers = {"Content-Type": "application/json;charset=utf-8"}
475 | data = {"msgtype": "text", "text": {"content": f"{title}\n\n{content}"}}
476 | response = requests.post(
477 | url=url, data=json.dumps(data), headers=headers, timeout=15
478 | ).json()
479 |
480 | if response["errcode"] == 0:
481 | print("企业微信机器人推送成功!")
482 | else:
483 | print("企业微信机器人推送失败!")
484 |
485 |
486 | def telegram_bot(title: str, content: str) -> None:
487 | """
488 | 使用 telegram 机器人 推送消息。
489 | """
490 | if not push_config.get("TG_BOT_TOKEN") or not push_config.get("TG_USER_ID"):
491 | print("tg 服务的 bot_token 或者 user_id 未设置!!\n取消推送")
492 | return
493 | print("tg 服务启动")
494 |
495 | if push_config.get("TG_API_HOST"):
496 | url = f"https://{push_config.get('TG_API_HOST')}/bot{push_config.get('TG_BOT_TOKEN')}/sendMessage"
497 | else:
498 | url = (
499 | f"https://api.telegram.org/bot{push_config.get('TG_BOT_TOKEN')}/sendMessage"
500 | )
501 | headers = {"Content-Type": "application/x-www-form-urlencoded"}
502 | payload = {
503 | "chat_id": str(push_config.get("TG_USER_ID")),
504 | "text": f"{title}\n\n{content}",
505 | "disable_web_page_preview": "true",
506 | }
507 | proxies = None
508 | if push_config.get("TG_PROXY_HOST") and push_config.get("TG_PROXY_PORT"):
509 | if push_config.get("TG_PROXY_AUTH") is not None and "@" not in push_config.get(
510 | "TG_PROXY_HOST"
511 | ):
512 | push_config["TG_PROXY_HOST"] = (
513 | push_config.get("TG_PROXY_AUTH")
514 | + "@"
515 | + push_config.get("TG_PROXY_HOST")
516 | )
517 | proxyStr = "http://{}:{}".format(
518 | push_config.get("TG_PROXY_HOST"), push_config.get("TG_PROXY_PORT")
519 | )
520 | proxies = {"http": proxyStr, "https": proxyStr}
521 | response = requests.post(
522 | url=url, headers=headers, params=payload, proxies=proxies
523 | ).json()
524 |
525 | if response["ok"]:
526 | print("tg 推送成功!")
527 | else:
528 | print("tg 推送失败!")
529 |
530 |
531 | def aibotk(title: str, content: str) -> None:
532 | """
533 | 使用 智能微秘书 推送消息。
534 | """
535 | if not push_config.get("AIBOTK_KEY") or not push_config.get("AIBOTK_TYPE") or not push_config.get("AIBOTK_NAME"):
536 | print("智能微秘书 的 AIBOTK_KEY 或者 AIBOTK_TYPE 或者 AIBOTK_NAME 未设置!!\n取消推送")
537 | return
538 | print("智能微秘书 服务启动")
539 |
540 | if push_config.get("AIBOTK_TYPE") == 'room':
541 | url = "https://api-bot.aibotk.com/openapi/v1/chat/room"
542 | data = {
543 | "apiKey": push_config.get("AIBOTK_KEY"),
544 | "roomName": push_config.get("AIBOTK_NAME"),
545 | "message": {"type": 1, "content": f'【青龙快讯】\n\n${title}\n${content}'}
546 | }
547 | else:
548 | url = "https://api-bot.aibotk.com/openapi/v1/chat/contact"
549 | data = {
550 | "apiKey": push_config.get("AIBOTK_KEY"),
551 | "name": push_config.get("AIBOTK_NAME"),
552 | "message": {"type": 1, "content": f'【青龙快讯】\n\n${title}\n${content}'}
553 | }
554 | body = json.dumps(data).encode(encoding="utf-8")
555 | headers = {"Content-Type": "application/json"}
556 | response = requests.post(url=url, data=body, headers=headers).json()
557 | print(response)
558 | if response["code"] == 0:
559 | print("智能微秘书 推送成功!")
560 | else:
561 | print(f'智能微秘书 推送失败!{response["error"]}')
562 |
563 |
564 | def one() -> str:
565 | """
566 | 获取一条一言。
567 | :return:
568 | """
569 | url = "https://v1.hitokoto.cn/"
570 | res = requests.get(url).json()
571 | return res["hitokoto"] + " ----" + res["from"]
572 |
573 |
574 | if push_config.get("BARK_PUSH"):
575 | notify_function.append(bark)
576 | if push_config.get("CONSOLE"):
577 | notify_function.append(console)
578 | if push_config.get("DD_BOT_TOKEN") and push_config.get("DD_BOT_SECRET"):
579 | notify_function.append(dingding_bot)
580 | if push_config.get("FSKEY"):
581 | notify_function.append(feishu_bot)
582 | if push_config.get("GOBOT_URL") and push_config.get("GOBOT_QQ"):
583 | notify_function.append(go_cqhttp)
584 | if push_config.get("GOTIFY_URL") and push_config.get("GOTIFY_TOKEN"):
585 | notify_function.append(gotify)
586 | if push_config.get("IGOT_PUSH_KEY"):
587 | notify_function.append(iGot)
588 | if push_config.get("PUSH_KEY"):
589 | notify_function.append(serverJ)
590 | if push_config.get("DEER_KEY"):
591 | notify_function.append(pushdeer)
592 | if push_config.get("CHAT_URL") and push_config.get("CHAT_TOKEN"):
593 | notify_function.append(chat)
594 | if push_config.get("PUSH_PLUS_TOKEN"):
595 | notify_function.append(pushplus_bot)
596 | if push_config.get("QMSG_KEY") and push_config.get("QMSG_TYPE"):
597 | notify_function.append(qmsg_bot)
598 | if push_config.get("QYWX_AM"):
599 | notify_function.append(wecom_app)
600 | if push_config.get("QYWX_KEY"):
601 | notify_function.append(wecom_bot)
602 | if push_config.get("TG_BOT_TOKEN") and push_config.get("TG_USER_ID"):
603 | notify_function.append(telegram_bot)
604 | if push_config.get("AIBOTK_KEY") and push_config.get("AIBOTK_TYPE") and push_config.get("AIBOTK_NAME"):
605 | notify_function.append(aibotk)
606 |
607 |
608 | def send(title: str, content: str) -> None:
609 | if not content:
610 | print(f"{title} 推送内容为空!")
611 | return
612 |
613 | hitokoto = push_config.get("HITOKOTO")
614 |
615 | text = one() if hitokoto else ""
616 | content += "\n\n" + text
617 |
618 | ts = [
619 | threading.Thread(target=mode, args=(title, content), name=mode.__name__)
620 | for mode in notify_function
621 | ]
622 | [t.start() for t in ts]
623 | [t.join() for t in ts]
624 |
625 |
626 | def main():
627 | send("title", "content")
628 |
629 |
630 | if __name__ == "__main__":
631 | main()
632 |
--------------------------------------------------------------------------------
/Utils/rsa_encrypt.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | # -------------------------------
4 | # @Author : github@limoruirui https://github.com/limoruirui
5 | # @Time : 2022/8/23 13:05
6 | # -------------------------------
7 | from Crypto.PublicKey.RSA import importKey, construct
8 | from Crypto.Cipher import PKCS1_v1_5
9 | from base64 import b64encode
10 |
11 |
12 | def public_key(rsaExponent, rsaModulus=10001):
13 | e = int(rsaExponent, 16)
14 | n = int(rsaModulus, 16) # snipped for brevity
15 | pubkey = construct((n, e)).export_key()
16 | return pubkey
17 |
18 |
19 | class RSA_Encrypt:
20 | def __init__(self, key):
21 | if isinstance(key, str):
22 | # 若提供的rsa公钥不为pem格式 则先将hex转化为pem格式
23 | # self.key = bytes.fromhex(key) if "PUBLIC KEY" not in key else key.encode()
24 | self.key = public_key(key) if "PUBLIC KEY" not in key else key.encode()
25 | else:
26 | print("提供的公钥格式不正确")
27 |
28 | def encrypt(self, data, b64=False):
29 | pub_key = importKey(self.key)
30 | cipher = PKCS1_v1_5.new(pub_key)
31 | rsa_text = cipher.encrypt(data.encode("utf8"))
32 | rsa_text = b64encode(rsa_text).decode() if b64 else rsa_text.hex()
33 | return rsa_text
34 |
--------------------------------------------------------------------------------
/Utils/telecom_login.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | # -------------------------------
4 | # @Author : github@limoruirui https://github.com/limoruirui
5 | # @Time : 2022/10/24 17:52
6 | # -------------------------------
7 | """
8 | 营业厅登录获取token loginAuthCipherAsymmertric参数解密参考自 github@QGCliveDavis https://github.com/QGCliveDavis 感谢大佬
9 | """
10 | from requests import post
11 | from datetime import datetime
12 | from xml.etree.ElementTree import XML
13 | from uuid import uuid4
14 | from Utils.encrypt_symmetric import Crypt
15 | from Utils.rsa_encrypt import RSA_Encrypt
16 | from Utils.tool import print_now
17 |
18 |
19 | class TelecomLogin:
20 | def __init__(self, account, pwd):
21 | self.account = account
22 | self.pwd = pwd
23 | self.deviceUid = uuid4().hex
24 |
25 | def login(self):
26 | url = "https://appgologin.189.cn:9031/login/client/userLoginNormal"
27 | timestamp = datetime.now().__format__("%Y%m%d%H%M%S")
28 | key = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBkLT15ThVgz6/NOl6s8GNPofd\nWzWbCkWnkaAm7O2LjkM1H7dMvzkiqdxU02jamGRHLX/ZNMCXHnPcW/sDhiFCBN18\nqFvy8g6VYb9QtroI09e176s+ZCtiv7hbin2cCTj99iUpnEloZm19lwHyo69u5UMi\nPMpq0/XKBO8lYhN/gwIDAQAB\n-----END PUBLIC KEY-----"
29 | body = {
30 | "headerInfos": {
31 | "code": "userLoginNormal",
32 | "timestamp": timestamp,
33 | "broadAccount": "",
34 | "broadToken": "",
35 | "clientType": "#9.6.1#channel50#iPhone 14 Pro Max#",
36 | "shopId": "20002",
37 | "source": "110003",
38 | "sourcePassword": "Sid98s",
39 | "token": "",
40 | "userLoginName": self.account
41 | },
42 | "content": {
43 | "attach": "test",
44 | "fieldData": {
45 | "loginType": "4",
46 | "accountType": "",
47 | "loginAuthCipherAsymmertric": RSA_Encrypt(key).encrypt(
48 | f"iPhone 14 15.4.{self.deviceUid[:12]}{self.account}{timestamp}{self.pwd}0$$$0.", b64=True),
49 | "deviceUid": self.deviceUid[:16],
50 | "phoneNum": self.get_phoneNum(self.account),
51 | "isChinatelecom": "0",
52 | "systemVersion": "15.4.0",
53 | "authentication": self.pwd
54 | }
55 | }
56 | }
57 | headers = {
58 | "user-agent": "iPhone 14 Pro Max/9.6.1",
59 |
60 | }
61 |
62 | data = post(url, headers=headers, json=body).json()
63 | code = data["responseData"]["resultCode"]
64 | if code != "0000":
65 | print_now("登陆失败, 接口日志" + str(data))
66 | return None
67 | self.token = data["responseData"]["data"]["loginSuccessResult"]["token"]
68 | self.userId = data["responseData"]["data"]["loginSuccessResult"]["userId"]
69 | return True
70 |
71 | def get_ticket(self):
72 | url = "https://appgologin.189.cn:9031/map/clientXML"
73 | body = f"\n\n getSingle
\n {datetime.now().__format__('%Y%m%d%H%M%S')}\n \n \n #9.6.1#channel50#iPhone 14 Pro Max#\n 20002\n 110003\n Sid98s\n {self.token}\n {self.account}\n \n \n test\n \n {self.encrypt_userid(self.userId)}\n 4a6862274835b451\n \n \n"
74 | headers = {
75 | "User-Agent": "samsung SM-G9750/9.4.0",
76 | "Content-Type": "text/xml; charset=utf-8",
77 | "Content-Length": "694",
78 | "Host": "appgologin.189.cn:9031",
79 | "Connection": "Keep-Alive",
80 | "Accept-Encoding": "gzip",
81 | "Pragma": "no-cache",
82 | "Cache-Control": "no-cache"
83 | }
84 | xml_data = post(url, headers=headers, data=body).text
85 | doc = XML(xml_data)
86 | secret_ticket = doc.find("ResponseData/Data/Ticket").text
87 | # print("secret: " + secret_ticket)
88 | ticket = self.decrypt_ticket(secret_ticket)
89 | # print("ticket: " + ticket)
90 | return ticket
91 |
92 | def main(self):
93 | if self.login() is None:
94 | return "10086"
95 | ticket = self.get_ticket()
96 | return ticket
97 |
98 | @staticmethod
99 | def get_phoneNum(phone):
100 | result = ""
101 | for i in phone:
102 | result += chr(ord(i) + 2)
103 | return result
104 |
105 | @staticmethod
106 | def decrypt_ticket(secret_ticket):
107 | key = "1234567`90koiuyhgtfrdewsaqaqsqde"
108 | iv = "\0\0\0\0\0\0\0\0"
109 | # ticket = des3_cbc_decrypt(key, bytes(TelecomLogin.process_text(secret_ticket)), iv)
110 | ticket = Crypt("des3", key, iv, "CBC").decrypt(TelecomLogin.process_text(secret_ticket))
111 | return ticket
112 |
113 | @staticmethod
114 | def encrypt_userid(userid):
115 | key = "1234567`90koiuyhgtfrdewsaqaqsqde"
116 | iv = bytes([0] * 8)
117 | targetId = Crypt("des3", key, iv, "CBC").encrypt(userid)
118 | return targetId
119 |
120 | @staticmethod
121 | def process_text(text):
122 | length = len(text) >> 1
123 | bArr = [0] * length
124 | if len(text) % 2 == 0:
125 | i2 = 0
126 | i3 = 0
127 | while i2 < length:
128 | i4 = i3 + 1
129 | indexOf = "0123456789abcdef0123456789ABCDEF".find(text[i3])
130 | if indexOf != -1:
131 | bArr[i2] = (((indexOf & 15) << 4) + ("0123456789abcdef0123456789ABCDEF".find(text[i4]) & 15))
132 | i2 += 1
133 | i3 = i4 + 1
134 | else:
135 | print("转化失败 大概率是明文输入错误")
136 | return bArr
137 |
--------------------------------------------------------------------------------
/Utils/telecom_login_by_yz.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | # -------------------------------
4 | # @Author : github@limoruirui https://github.com/limoruirui
5 | # @Time : 2022/10/24 17:52
6 | # -------------------------------
7 | """
8 | 营业厅登录获取token loginAuthCipherAsymmertric参数解密参考自 github@QGCliveDavis https://github.com/QGCliveDavis 感谢大佬
9 | """
10 | from requests import post
11 | from datetime import datetime
12 | from xml.etree.ElementTree import XML
13 | from uuid import uuid4
14 | from Utils.encrypt_symmetric import Crypt
15 | from Utils.rsa_encrypt import RSA_Encrypt
16 | from Utils.tool import print_now
17 |
18 | class TelecomLogin:
19 | def __init__(self, account, pwd):
20 | self.account = account
21 | self.pwd = pwd
22 | self.deviceUid = uuid4().hex
23 | def login(self):
24 | url = "https://appgologin.189.cn:9031/login/client/userLoginNormal"
25 | timestamp = datetime.now().__format__("%Y%m%d%H%M%S")
26 | key = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBkLT15ThVgz6/NOl6s8GNPofd\nWzWbCkWnkaAm7O2LjkM1H7dMvzkiqdxU02jamGRHLX/ZNMCXHnPcW/sDhiFCBN18\nqFvy8g6VYb9QtroI09e176s+ZCtiv7hbin2cCTj99iUpnEloZm19lwHyo69u5UMi\nPMpq0/XKBO8lYhN/gwIDAQAB\n-----END PUBLIC KEY-----"
27 | body = {
28 | "headerInfos": {
29 | "code": "userLoginNormal",
30 | "timestamp": timestamp,
31 | "broadAccount": "",
32 | "broadToken": "",
33 | "clientType": "#9.6.1#channel50#iPhone 14 Pro Max#",
34 | "shopId": "20002",
35 | "source": "110003",
36 | "sourcePassword": "Sid98s",
37 | "token": "",
38 | "userLoginName": self.account
39 | },
40 | "content": {
41 | "attach": "test",
42 | "fieldData": {
43 | "loginType": "4",
44 | "accountType": "",
45 | "loginAuthCipherAsymmertric": RSA_Encrypt(key).encrypt(f"iPhone 14 15.4.{self.deviceUid[:12]}{self.account}{timestamp}{self.pwd}0$$$0.", b64=True),
46 | "deviceUid": self.deviceUid[:16],
47 | "phoneNum": self.get_phoneNum(self.account),
48 | "isChinatelecom": "0",
49 | "systemVersion": "15.4.0",
50 | "authentication": self.pwd
51 | }
52 | }
53 | }
54 | headers = {
55 | "user-agent": "iPhone 14 Pro Max/9.6.1",
56 |
57 | }
58 |
59 | data = post(url, headers=headers, json=body).json()
60 | code = data["responseData"]["resultCode"]
61 | if code != "0000":
62 | print_now("账号【"+self.account+"】,登陆失败,可能密码错误, 接口日志" + str(data))
63 | return None
64 | self.token = data["responseData"]["data"]["loginSuccessResult"]["token"]
65 | self.userId = data["responseData"]["data"]["loginSuccessResult"]["userId"]
66 | return True
67 |
68 | def get_ticket(self):
69 | url = "https://appgologin.189.cn:9031/map/clientXML"
70 | body = f"\n\n getSingle
\n {datetime.now().__format__('%Y%m%d%H%M%S')}\n \n \n #9.6.1#channel50#iPhone 14 Pro Max#\n 20002\n 110003\n Sid98s\n {self.token}\n {self.account}\n \n \n test\n \n {self.encrypt_userid(self.userId)}\n 4a6862274835b451\n \n \n"
71 | headers = {
72 | "User-Agent": "samsung SM-G9750/9.4.0",
73 | "Content-Type": "text/xml; charset=utf-8",
74 | "Content-Length": "694",
75 | "Host": "appgologin.189.cn:9031",
76 | "Connection": "Keep-Alive",
77 | "Accept-Encoding": "gzip",
78 | "Pragma": "no-cache",
79 | "Cache-Control": "no-cache"
80 | }
81 | xml_data = post(url, headers=headers, data=body).text
82 | doc = XML(xml_data)
83 | secret_ticket = doc.find("ResponseData/Data/Ticket").text
84 | # print("secret: " + secret_ticket)
85 | ticket = self.decrypt_ticket(secret_ticket)
86 | # print("ticket: " + ticket)
87 | return ticket, self.token
88 | def main(self):
89 | if self.login() is None:
90 | return "", ""
91 | userLoginInfo = self.get_ticket()
92 | return userLoginInfo
93 | @staticmethod
94 | def get_phoneNum(phone):
95 | result = ""
96 | for i in phone:
97 | result += chr(ord(i) + 2)
98 | return result
99 | @staticmethod
100 | def decrypt_ticket(secret_ticket):
101 | key = "1234567`90koiuyhgtfrdewsaqaqsqde"
102 | iv = "\0\0\0\0\0\0\0\0"
103 | # ticket = des3_cbc_decrypt(key, bytes(TelecomLogin.process_text(secret_ticket)), iv)
104 | ticket = Crypt("des3", key, iv, "CBC").decrypt(TelecomLogin.process_text(secret_ticket))
105 | return ticket
106 | @staticmethod
107 | def encrypt_userid(userid):
108 | key = "1234567`90koiuyhgtfrdewsaqaqsqde"
109 | iv = bytes([0] * 8)
110 | targetId = Crypt("des3", key, iv, "CBC").encrypt(userid)
111 | return targetId
112 |
113 | @staticmethod
114 | def process_text(text):
115 | length = len(text) >> 1
116 | bArr = [0] * length
117 | if len(text) % 2 == 0:
118 | i2 = 0
119 | i3 = 0
120 | while i2 < length:
121 | i4 = i3 + 1
122 | indexOf = "0123456789abcdef0123456789ABCDEF".find(text[i3])
123 | if indexOf != -1:
124 | bArr[i2] = (((indexOf & 15) << 4) + ("0123456789abcdef0123456789ABCDEF".find(text[i4]) & 15))
125 | i2 += 1
126 | i3 = i4 + 1
127 | else:
128 | print("转化失败 大概率是明文输入错误")
129 | return bArr
--------------------------------------------------------------------------------
/Utils/tool.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | # -- coding: utf-8 --
3 | # -------------------------------
4 | # @Author : github@limoruirui https://github.com/limoruirui
5 | # @Time : 2022/8/22 17:56
6 | # -------------------------------
7 | """
8 | 封装一些工具
9 | """
10 | from hashlib import md5 as md5Encode, sha1 as sha1Encode
11 | from hmac import new
12 | from random import choice, randint
13 | from string import digits, ascii_lowercase, ascii_uppercase
14 | from time import sleep, time
15 | from datetime import datetime, timedelta
16 | from sys import stdout
17 | from os import environ
18 | from json import load
19 |
20 |
21 | # 生成随机字符串
22 | def uuid(num, upper=False):
23 | str = ''
24 | if upper:
25 | for i in range(num):
26 | str += choice(digits + ascii_lowercase + ascii_uppercase)
27 | else:
28 | for i in range(num):
29 | str += choice(digits + ascii_lowercase)
30 | return str
31 |
32 |
33 | # 修改print方法 避免某些环境下python执行print 不会去刷新缓存区导致信息第一时间不及时输出
34 | def print_now(content):
35 | print(content)
36 | stdout.flush()
37 |
38 |
39 | def get_ua():
40 | with open("../user_agent.json", "rb") as f:
41 | ua_list = load(f)["Chrome"]
42 | ua = choice(ua_list)
43 | return ua
44 |
45 |
46 | # 随机休眠时长 若为0时区 TimeZone为真
47 | def random_sleep(min_time=300, max_time=5400, TimeZone=True):
48 | random_time = randint(min_time, max_time)
49 | print_now(f"随机等待{random_time}秒")
50 | sleep(random_time)
51 | now_time = (datetime.now() + timedelta(hours=8)).__format__("%Y-%m-%d %H:%M:%S")
52 | if TimeZone:
53 | now_time = (datetime.now()).__format__("%Y-%m-%d %H:%M:%S")
54 | print_now(f"等待结束.开始执行 现在时间是------{now_time} ------------")
55 |
56 |
57 | def timestamp(short=False):
58 | if short:
59 | return int(round(time()))
60 | return int(round(time() * 1000))
61 |
62 |
63 | # md5
64 | def md5(data):
65 | if isinstance(data, str):
66 | data = data.encode("utf8")
67 | m = md5Encode(data)
68 | return m.hexdigest()
69 |
70 | def sha1(data):
71 | if isinstance(data, str):
72 | data = data.encode("utf8")
73 | elif isinstance(data, list):
74 | data = bytes(data)
75 | m = sha1Encode(data)
76 | return m.hexdigest()
77 | # hmac sha1
78 | def hmac_sha1(data, key):
79 | hmac_code = new(key.encode(), data.encode(), sha1Encode)
80 | return hmac_code.hexdigest()
81 |
82 |
83 | # 封装读取环境变量的方法
84 | def get_environ(key, default="", output=True):
85 | def no_read():
86 | if output:
87 | print_now(f"未填写环境变量 {key} 请添加")
88 | return default
89 | return environ.get(key) if environ.get(key) else no_read()
90 |
--------------------------------------------------------------------------------
/Utils/utils.py:
--------------------------------------------------------------------------------
1 | import pathlib
2 | import random
3 | import hashlib
4 | import yaml
5 | import time
6 |
7 |
8 | def random_sign(mask: str, figure: int) -> str:
9 | sign = ''
10 | while len(sign) < figure:
11 | rIndex = random.randint(0, len(mask) - 1)
12 | sign += mask[rIndex]
13 | return sign
14 |
15 |
16 | # 生成时间戳
17 | def create_timestamp(figure: int = 10) -> str:
18 | timestamp = time.time()
19 | power = figure - 10
20 | return str(int(timestamp * (10 ** power)))
21 |
22 |
23 | # MD5加密
24 | def md5_crypto(text: str, short: bool = False, upper: bool = True, coding: str = 'utf8') -> str:
25 | ciphertext = hashlib.md5(str(text).encode(coding)).hexdigest()
26 | if short:
27 | ciphertext = ciphertext[8:-8]
28 | return ciphertext.upper() if upper else ciphertext
29 |
30 |
31 | # 读取yaml文件
32 | def read_yaml(path: str = 'config.yaml') -> dict:
33 | with open(pathlib.Path(path), 'rb') as stream:
34 | return yaml.safe_load(stream)
35 |
36 |
37 | # 读取多文档yaml文件
38 | def read_multiple_yaml(path: str = 'config.yaml') -> list:
39 | arr = []
40 | with open(pathlib.Path(path), 'rb') as stream:
41 | for el in yaml.safe_load_all(stream):
42 | arr.append(el)
43 | return arr
44 |
45 |
46 | # 写入yaml文件
47 | def write_yaml(obj: dict, path: str = 'config.yaml'):
48 | with open(pathlib.Path(path), 'w') as stream:
49 | return yaml.dump(obj, stream)
50 |
51 |
52 | # 请求URL queryParams 转Python Dict
53 | def format_params(url: str) -> dict:
54 | params = {}
55 | query = url.split('?')[1].split('&')
56 | for kv in query:
57 | key = kv.split('=')[0]
58 | value = kv.split('=')[1]
59 | params.update({key: value})
60 | return params
61 |
--------------------------------------------------------------------------------
/YunMusic/check_in.py:
--------------------------------------------------------------------------------
1 | import requests, base64, json, hashlib
2 | from Crypto.Cipher import AES
3 |
4 |
5 | def encrypt(key, text):
6 | cryptor = AES.new(key.encode('utf8'), AES.MODE_CBC, b'0102030405060708')
7 | length = 16
8 | count = len(text.encode('utf-8'))
9 | if (count % length != 0):
10 | add = length - (count % length)
11 | else:
12 | add = 16
13 | pad = chr(add)
14 | text1 = text + (pad * add)
15 | ciphertext = cryptor.encrypt(text1.encode('utf8'))
16 | cryptedStr = str(base64.b64encode(ciphertext), encoding='utf-8')
17 | return cryptedStr
18 |
19 |
20 | def md5(str):
21 | hl = hashlib.md5()
22 | hl.update(str.encode(encoding='utf-8'))
23 | return hl.hexdigest()
24 |
25 |
26 | def protect(text):
27 | return {"params": encrypt('TA3YiYCfY2dDJQgg', encrypt('0CoJUm6Qyw8W8jud', text)),
28 | "encSecKey": "84ca47bca10bad09a6b04c5c927ef077d9b9f1e37098aa3eac6ea70eb59df0aa28b691b7e75e4f1f9831754919ea784c8f74fbfadf2898b0be17849fd656060162857830e241aba44991601f137624094c114ea8d17bce815b0cd4e5b8e2fbaba978c6d1d14dc3d1faf852bdd28818031ccdaaa13a6018e1024e2aae98844210"}
29 |
30 |
31 | s = requests.Session()
32 | header = {}
33 | url = "https://music.163.com/weapi/login/cellphone"
34 | url2 = "https://music.163.com/weapi/point/dailyTask"
35 | url3 = "https://music.163.com/weapi/v1/discovery/recommend/resource"
36 | logindata = {
37 | "phone": input(),
38 | "countrycode": "86",
39 | "password": md5(input()),
40 | "rememberLogin": "true",
41 | }
42 | headers = {
43 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36',
44 | "Referer": "http://music.163.com/",
45 | "Accept-Encoding": "gzip, deflate",
46 | }
47 | headers2 = {
48 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36',
49 | "Referer": "http://music.163.com/",
50 | "Accept-Encoding": "gzip, deflate",
51 | "Cookie": "os=pc; osver=Microsoft-Windows-10-Professional-build-10586-64bit; appver=2.0.3.131777; channel=netease; __remember_me=true;"
52 | }
53 |
54 | res = s.post(url=url, data=protect(json.dumps(logindata)), headers=headers2)
55 | tempcookie = res.cookies
56 | object = json.loads(res.text)
57 | if object['code'] == 200:
58 | print("登录成功!")
59 | else:
60 | print("登录失败!请检查密码是否正确!" + str(object['code']))
61 | exit(object['code'])
62 |
63 | res = s.post(url=url2, data=protect('{"type":0}'), headers=headers)
64 | object = json.loads(res.text)
65 | if object['code'] != 200 and object['code'] != -2:
66 | print("签到时发生错误:" + object['msg'])
67 | else:
68 | if object['code'] == 200:
69 | print("签到成功,经验+" + str(object['point']))
70 | else:
71 | print("重复签到")
72 |
73 | res = s.post(url=url3,
74 | data=protect('{"csrf_token":"' + requests.utils.dict_from_cookiejar(tempcookie)['__csrf'] + '"}'),
75 | headers=headers)
76 | object = json.loads(res.text, strict=False)
77 | for x in object['recommend']:
78 | url = 'https://music.163.com/weapi/v3/playlist/detail?csrf_token=' + requests.utils.dict_from_cookiejar(tempcookie)[
79 | '__csrf']
80 | data = {
81 | 'id': x['id'],
82 | 'n': 1000,
83 | 'csrf_token': requests.utils.dict_from_cookiejar(tempcookie)['__csrf'],
84 | }
85 | res = s.post(url, protect(json.dumps(data)), headers=headers)
86 | object = json.loads(res.text, strict=False)
87 | buffer = []
88 | count = 0
89 | for j in object['playlist']['trackIds']:
90 | data2 = {}
91 | data2["action"] = "play"
92 | data2["json"] = {}
93 | data2["json"]["download"] = 0
94 | data2["json"]["end"] = "playend"
95 | data2["json"]["id"] = j["id"]
96 | data2["json"]["sourceId"] = ""
97 | data2["json"]["time"] = "240"
98 | data2["json"]["type"] = "song"
99 | data2["json"]["wifi"] = 0
100 | buffer.append(data2)
101 | count += 1
102 | if count >= 310:
103 | break
104 | if count >= 310:
105 | break
106 | url = "http://music.163.com/weapi/feedback/weblog"
107 | postdata = {
108 | "logs": json.dumps(buffer)
109 | }
110 | res = s.post(url, protect(json.dumps(postdata)))
111 | object = json.loads(res.text, strict=False)
112 | if object['code'] == 200:
113 | print("刷单成功!共" + str(count) + "首")
114 | exit()
115 | else:
116 | print("发生错误:" + str(object['code']) + object['message'])
117 | exit(object['code'])
118 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
2 | beautifulsoup4
3 | PyYAML
4 | python-dotenv
5 | pycryptodome
6 | ddddocr
--------------------------------------------------------------------------------