diff --git a/chinatelecom.py b/chinatelecom.py index bb1e0d7..34da1b9 100644 --- a/chinatelecom.py +++ b/chinatelecom.py @@ -1,118 +1,127 @@ -# !/usr/bin/python3 -# -- coding: utf-8 -- -# cron: 53 59 09,13 * * * -# const $ = new Env("电信金豆换话费"); -import requests +""" +新电信抢话费 + +群里发的,未测试好,自测 +修改内容如下“ +1.删除内置的一个手机账号 +2.修改环境变量名保持和拉菲电信金豆本环境变量一致 +3.恢复瑞数通杀.js调用地址,确实也不知道是啥。398、399行注释 + +环境变量chinaTelecomAccount,值为:账号#密码 + +cron: 57 9,13,23 * * * +const $ = new Env("新电信抢话费"); + +""" +#!/usr/bin/env python3 +import os import re +import sys +import ssl import time import json -import random -import datetime -import base64 -import threading -import ssl import execjs -import os -import sys - -from bs4 import BeautifulSoup - +import base64 +import random +import certifi +import aiohttp +import asyncio +import certifi +import datetime +import requests +import binascii +from lxml import etree +from http import cookiejar +from Crypto.Cipher import AES +from Crypto.Cipher import DES3 from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 -from Crypto.Cipher import DES3 from Crypto.Util.Padding import pad, unpad -from Crypto.Util.strxor import strxor -from Crypto.Cipher import AES -from http import cookiejar # Python 2: import cookielib as cookiejar -from requests.adapters import HTTPAdapter -from requests.packages.urllib3.util.ssl_ import create_urllib3_context +from aiohttp import ClientSession, TCPConnector +from concurrent.futures import ThreadPoolExecutor +import subprocess + +run_num=os.environ.get('reqNUM') or "2" + +MAX_RETRIES = 3 +RATE_LIMIT = 10 # 每秒请求数限制 + +class RateLimiter: + def __init__(self, rate_limit): + self.rate_limit = rate_limit + self.tokens = rate_limit + self.updated_at = time.monotonic() + + async def acquire(self): + while self.tokens < 1: + self.add_new_tokens() + await asyncio.sleep(0.1) + self.tokens -= 1 + + def add_new_tokens(self): + now = time.monotonic() + time_since_update = now - self.updated_at + new_tokens = time_since_update * self.rate_limit + if new_tokens > 1: + self.tokens = min(self.tokens + new_tokens, self.rate_limit) + self.updated_at = now + +class AsyncSessionManager: + def __init__(self): + self.session = None + self.connector = None + + async def __aenter__(self): + ssl_context = ssl.create_default_context(cafile=certifi.where()) + ssl_context.set_ciphers('DEFAULT@SECLEVEL=1') + self.connector = TCPConnector(ssl=ssl_context, limit=1000) + self.session = ClientSession(connector=self.connector) + return self.session + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.session.close() + await self.connector.close() + +async def retry_request(session, method, url, **kwargs): + for attempt in range(MAX_RETRIES): + try: + await asyncio.sleep(1) + async with session.request(method, url, **kwargs) as response: + return await response.json() + # return await response.json() + + except (aiohttp.ClientConnectionError, aiohttp.ServerTimeoutError) as e: + print(f"请求失败,第 {attempt + 1} 次重试: {e}") + if attempt == MAX_RETRIES - 1: + raise + await asyncio.sleep(2 ** attempt) class BlockAll(cookiejar.CookiePolicy): return_ok = set_ok = domain_return_ok = path_return_ok = lambda self, *args, **kwargs: False netscape = True rfc2965 = hide_cookie2 = False - -def printn(m): - print(f'\n{m}') -ORIGIN_CIPHERS = ('DEFAULT@SECLEVEL=1') -ip_list = [] -class DESAdapter(HTTPAdapter): - def __init__(self, *args, **kwargs): - """ - A TransportAdapter that re-enables 3DES support in Requests. - """ - CIPHERS = ORIGIN_CIPHERS.split(':') - random.shuffle(CIPHERS) - CIPHERS = ':'.join(CIPHERS) - self.CIPHERS = CIPHERS + ':!aNULL:!eNULL:!MD5' - super().__init__(*args, **kwargs) - +def printn(m): + current_time = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3] + print(f'\n[{current_time}] {m}') + +context = ssl.create_default_context() +context.set_ciphers('DEFAULT@SECLEVEL=1') # 低安全级别0/1 +context.check_hostname = False # 禁用主机 +context.verify_mode = ssl.CERT_NONE # 禁用证书 + +class DESAdapter(requests.adapters.HTTPAdapter): def init_poolmanager(self, *args, **kwargs): - context = create_urllib3_context(ciphers=self.CIPHERS) kwargs['ssl_context'] = context - return super(DESAdapter, self).init_poolmanager(*args, **kwargs) - - def proxy_manager_for(self, *args, **kwargs): - context = create_urllib3_context(ciphers=self.CIPHERS) - kwargs['ssl_context'] = context - return super(DESAdapter, self).proxy_manager_for(*args, **kwargs) - + return super().init_poolmanager(*args, **kwargs) requests.packages.urllib3.disable_warnings() -ssl_context = ssl.create_default_context() -ssl_context.check_hostname = False -ssl_context.verify_mode = ssl.CERT_NONE -ssl_context.set_ciphers('DEFAULT@SECLEVEL=0') +# urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) ss = requests.session() -ss.ssl=ssl_context -ss.headers={"User-Agent":"Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36","Referer":"https://wapact.189.cn:9001/JinDouMall/JinDouMall_independentDetails.html"} -ss.mount('https://', DESAdapter()) -yc = 0.1 -wt = 0 -kswt = -3 -yf = datetime.datetime.now().strftime("%Y%m") - - -jp = {"9":{},"12":{},"13":{},"23":{}} - - -try: - with open('telecom_doudou.log') as fr: - dhjl = json.load(fr) -except: - dhjl = {} -if yf not in dhjl: - dhjl[yf] = {} - - - - -wxp={} -errcode = { - "0":"兑换成功", - "412":"兑换次数已达上限", - "413":"商品已兑完", - "420":"未知错误", - "410":"该活动已失效~", - "Y0001":"当前等级不足,去升级兑当前话费", - "Y0002":"使用翼相连网络600分钟或连接并拓展网络500分钟可兑换此奖品", - "Y0003":"使用翼相连共享流量400M或共享WIFI:2GB可兑换此奖品", - "Y0004":"使用翼相连共享流量2GB可兑换此奖品", - "Y0005":"当前等级不足,去升级兑当前话费", - "E0001":"您的网龄不足10年,暂不能兑换" -} - - - - - - - - - - -#加密参数 +ss.headers={"User-Agent":"Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36","Referer":"https://wapact.189.cn:9001/JinDouMall/JinDouMall_independentDetails.html"} +ss.mount('https://', DESAdapter()) +ss.cookies.set_policy(BlockAll()) +runTime = 0 key = b'1234567`90koiuyhgtfrdews' iv = 8 * b'\0' @@ -124,16 +133,22 @@ public_key_data = '''-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+ugG5A8cZ3FqUKDwM57GM4io6JGcStivT8UdGt67PEOihLZTw3P7371+N47PrmsCpnTRzbTgcupKtUv8ImZalYk65dU8rjC/ridwhw9ffW2LBwvkEnDkkKKRi2liWIItDftJVBiWOh17o6gfbPoNrWORcAdcbpk2L+udld5kZNwIDAQAB -----END PUBLIC KEY-----''' +def get_first_three(value): + # 处理数字情况 + if isinstance(value, (int, float)): + return int(str(value)[:3]) + elif isinstance(value, str): + return str(value)[:3] + else: + raise TypeError("error") -def t(h): +def run_Time(hour,miute,second): date = datetime.datetime.now() - date_zero = datetime.datetime.now().replace(year=date.year, month=date.month, day=date.day, hour=h, minute=59, second=59) + date_zero = datetime.datetime.now().replace(year=date.year, month=date.month, day=date.day, hour=hour, minute=miute, second=second) date_zero_time = int(time.mktime(date_zero.timetuple())) return date_zero_time - - -def encrypt(text): +def encrypt(text): cipher = DES3.new(key, DES3.MODE_CBC, iv) ciphertext = cipher.encrypt(pad(text.encode(), DES3.block_size)) return ciphertext.hex() @@ -143,348 +158,322 @@ def decrypt(text): cipher = DES3.new(key, DES3.MODE_CBC, iv) plaintext = unpad(cipher.decrypt(ciphertext), DES3.block_size) return plaintext.decode() - - - + def b64(plaintext): public_key = RSA.import_key(public_key_b64) cipher = PKCS1_v1_5.new(public_key) ciphertext = cipher.encrypt(plaintext.encode()) return base64.b64encode(ciphertext).decode() - + def encrypt_para(plaintext): + if not isinstance(plaintext, str): + plaintext = json.dumps(plaintext) public_key = RSA.import_key(public_key_data) cipher = PKCS1_v1_5.new(public_key) ciphertext = cipher.encrypt(plaintext.encode()) - return ciphertext.hex() - - + return binascii.hexlify(ciphertext).decode() + def encode_phone(text): encoded_chars = [] for char in text: encoded_chars.append(chr(ord(char) + 2)) return ''.join(encoded_chars) -def ophone(t): - key = b'34d7cb0bcdf07523' - utf8_key = key.decode('utf-8') - utf8_t = t.encode('utf-8') - cipher = AES.new(key, AES.MODE_ECB) - ciphertext = cipher.encrypt(pad(utf8_t, AES.block_size)) - return ciphertext.hex() -def send(uid,content): - r = requests.post('https://wxpusher.zjiecode.com/api/send/message',json={"appToken":"AT_3hr0wdZn5QzPNBbpTHFXawoDIsSUmPkN","content":content,"contentType":1,"uids":[uid]}).json() - return r - - +def getApiTime(api_url): + try: + with requests.get(api_url) as response: + if(not response or not response.text): + return time.time() + json_data = json.loads(response.text) + if (json_data.get("api")and json_data.get("api")not in("time") ): + timestamp_str = json_data.get('data', {}).get('t', '') + else: + timestamp_str = json_data.get('currentTime', {}) + timestamp = int(timestamp_str) / 1000.0 # 将毫秒转为秒 + difftime=time.time()-timestamp + return difftime; + except Exception as e: + print(f"获取时间失败: {e}") + return 0; + + def userLoginNormal(phone,password): alphabet = 'abcdef0123456789' uuid = [''.join(random.sample(alphabet, 8)),''.join(random.sample(alphabet, 4)),'4'+''.join(random.sample(alphabet, 3)),''.join(random.sample(alphabet, 4)),''.join(random.sample(alphabet, 12))] timestamp=datetime.datetime.now().strftime("%Y%m%d%H%M%S") loginAuthCipherAsymmertric = 'iPhone 14 15.4.' + uuid[0] + uuid[1] + phone + timestamp + password[:6] + '0$$$0.' - - r = ss.post('https://appgologin.189.cn:9031/login/client/userLoginNormal',json={"headerInfos": {"code": "userLoginNormal", "timestamp": timestamp, "broadAccount": "", "broadToken": "", "clientType": "#9.6.1#channel50#iPhone 14 Pro Max#", "shopId": "20002", "source": "110003", "sourcePassword": "Sid98s", "token": "", "userLoginName": phone}, "content": {"attach": "test", "fieldData": {"loginType": "4", "accountType": "", "loginAuthCipherAsymmertric": b64(loginAuthCipherAsymmertric), "deviceUid": uuid[0] + uuid[1] + uuid[2], "phoneNum": encode_phone(phone), "isChinatelecom": "0", "systemVersion": "15.4.0", "authentication": password}}}).json() - - - + r = ss.post('https://appgologin.189.cn:9031/login/client/userLoginNormal',json={"headerInfos": {"code": "userLoginNormal", "timestamp": timestamp, "broadAccount": "", "broadToken": "", "clientType": "#9.6.1#channel50#iPhone 14 Pro Max#", "shopId": "20002", "source": "110003", "sourcePassword": "Sid98s", "token": "", "userLoginName": phone}, "content": {"attach": "test", "fieldData": {"loginType": "4", "accountType": "", "loginAuthCipherAsymmertric": b64(loginAuthCipherAsymmertric), "deviceUid": uuid[0] + uuid[1] + uuid[2], "phoneNum": encode_phone(phone), "isChinatelecom": "0", "systemVersion": "15.4.0", "authentication": password}}},verify=certifi.where()).json() l = r['responseData']['data']['loginSuccessResult'] - if l: - load_token[phone] = l - with open(load_token_file, 'w') as f: - json.dump(load_token, f) - ticket = get_ticket(phone,l['userId'],l['token']) + ticket = get_ticket(phone,l['userId'],l['token']) return ticket - return False -def get_ticket(phone,userId,token): - r = ss.post('https://appgologin.189.cn:9031/map/clientXML',data='getSingle'+datetime.datetime.now().strftime("%Y%m%d%H%M%S")+'#9.6.1#channel50#iPhone 14 Pro Max#20002110003Sid98s'+token+''+phone+'test'+encrypt(userId)+'4a6862274835b451',headers={'user-agent': 'CtClient;10.4.1;Android;13;22081212C;NTQzNzgx!#!MTgwNTg1'}) - #printn(phone, '获取ticket', re.findall('(.*?)',r.text)[0]) - +async def exchangeForDay(phone, session, run_num, rid, stime): + async def delayed_conversion(delay): + await asyncio.sleep(delay) + await conversionRights(phone, rid,session) + tasks = [asyncio.create_task(delayed_conversion(i * stime)) for i in range(int(run_num))] + await asyncio.gather(*tasks) +def get_ticket(phone,userId,token): + r = ss.post('https://appgologin.189.cn:9031/map/clientXML',data='getSingle'+datetime.datetime.now().strftime("%Y%m%d%H%M%S")+'#9.6.1#channel50#iPhone 14 Pro Max#20002110003Sid98s'+token+''+phone+'test'+encrypt(userId)+'4a6862274835b451',headers={'user-agent': 'CtClient;10.4.1;Android;13;22081212C;NTQzNzgx!#!MTgwNTg1'},verify=certifi.where()) tk = re.findall('(.*?)',r.text) - if len(tk) == 0: + if len(tk) == 0: return False - - return decrypt(tk[0]) - - -def queryInfo(phone,s): - global rs - a = 1 - while a < 10: - if rs: - bd = js.call('main').split('=') - ck[bd[0]] = bd[1] - - r = s.get('https://wapact.189.cn:9001/gateway/golden/api/queryInfo',cookies=ck).json() - - try: - printn(f'{phone} 金豆余额 {r["biz"]["amountTotal"]}') - amountTotal= r["biz"]["amountTotal"] - except: - amountTotal = 0 - if amountTotal< 3000: - if rs == 1: - bd = js.call('main').split('=') - ck [bd[0]] = bd[1] - - - res = s.post('http://wapact.189.cn:9000/gateway/stand/detail/exchange',json={"activityId":jdaid},cookies=ck).text - - if '$_ts=window' in res: - first_request() - rs = 1 - - time.sleep(3) - else: - return r - a += 1 - - - return r - - -def exchange(phone,s,title,aid, uid): - +async def exchange(s, phone, title, aid,jsexec, ckvalue): try: - bd = js.call('main').split('=') - ck [bd[0]] = bd[1] - r = s.post('https://wapact.189.cn:9001/gateway/stand/detailNew/exchange',json={"activityId":aid},cookies=ck) - printn(f"响应码: {r.status_code}") - - if '$_ts=window' in r.text: - - first_request(r.text) - return - r = r.json() - - if r["code"] == 0: - if r["biz"] != {} and r["biz"]["resultCode"] in errcode: - #printn(str(datetime.datetime.now())[11:22], phone, title,errcode[r["biz"]["resultCode"]]) - printn(f'{str(datetime.datetime.now())[11:22]} {phone} {title} {errcode[r["biz"]["resultCode"]]}') - + url="https://wapact.189.cn:9001/gateway/standExchange/detailNew/exchange" + # getck = await asyncio.to_thread(jsexec.call, "getck") # 两种方式,一种用ck,一种用后缀 + # getck = getck.split(';')[0].split('=') + # ckvalue[getck[0]] = getck[1] - if r["biz"]["resultCode"] in ["0","412"]: - if r["biz"]["resultCode"] == "0": - msg = phone+":"+title+"兑换成功" - requests.post('http://106.53.145.222:81/work.php', json={"msgtype": "text","msg": msg}) - send(uid, msg) - if phone not in dhjl[yf][title]: - dhjl[yf][title] += "#"+phone - with open('telecom_doudou.log', 'w') as f: - json.dump(dhjl, f, ensure_ascii=False) - - - else: - #printn(str(datetime.datetime.now())[11:22], phone, r["message"]) - printn(f'{str(datetime.datetime.now())[11:22]} {phone} {r}') - + # async with s.post(url, cookies=ckvalue, json={"activityId": aid}) as response: + + # 通过 retry_request 实现重试机制 + # response = await retry_request(s, 'POST', get_url, cookies=ckvalue, json={"activityId": aid}) + + get_url = await asyncio.to_thread(jsexec.call,"getUrl", "POST",url) + async with s.post(get_url, cookies=ckvalue, json={"activityId": aid}) as response: + pass except Exception as e: - #print(e) - pass - - -def dh(phone,s,title,aid,wt, uid): - - while wt > time.time(): - pass - - printn(f"{str(datetime.datetime.now())[11:22]} {phone} {title} 开始兑换") - - if rs: - bd = js.call('main').split('=') - ck [bd[0]] = bd[1] - for cs in range(cfcs): - threading.Thread(target=exchange,args=(phone,s,title,aid, uid)).start() - #time.sleep(5) + print(e) -def lottery(s): - for cishu in range(3): +async def check(s,item,ckvalue): + checkGoods = s.get('https://wapact.189.cn:9001/gateway/stand/detailNew/check?activityId=' + item, cookies=ckvalue).json() + return checkGoods + +async def conversionRights(phone, aid, session): + try: + # 获取 Ruishu cookies + ruishu_cookies = get_ruishu_cookies() + if not ruishu_cookies: + print(f"{get_first_three(phone)}: 无法获取 Ruishu cookies") + return + + value = { + "phone": phone, + "rightsId": aid + } + paraV = encrypt_para(value) + + printn(f"{get_first_three(phone)}:开始兑换") + + # 使用 Ruishu cookies 发送请求 + response = session.post( + 'https://wapside.189.cn:9001/jt-sign/paradise/conversionRights', + json={"para": paraV}, + cookies=ruishu_cookies + ) + + login = response.json() + printn(f"{get_first_three(phone)}:{login}") + + if '兑换成功' in response.text: + QLAPI.notify(get_first_three(phone), login['resoultMsg']) + exit(0) + + except Exception as e: + printn(f"{get_first_three(phone)}: 兑换请求发生错误: {str(e)}") + # 可以选择是否在这里添加重试逻辑 + +async def getLevelRightsList(phone, session): + try: + # 获取 Ruishu cookies + ruishu_cookies = get_ruishu_cookies() + if not ruishu_cookies: + print("无法获取 Ruishu cookies") + return None + + value = { + "phone": phone + } + paraV = encrypt_para(value) + + # 使用 Ruishu cookies 发送请求 + response = session.post( + 'https://wapside.189.cn:9001/jt-sign/paradise/getLevelRightsList', + json={"para": paraV}, + cookies=ruishu_cookies + ) + + data = response.json() + if data.get('code') == 401: + print(f"获取失败:{data},原因大概是sign过期了") + return None + + current_level = int(data['currentLevel']) + key_name = 'V' + str(current_level) + ids = [item['id'] for item in data.get(key_name, []) if item.get('name') == '话费'] + return ids + + except Exception as e: + print(f"获取失败,重试一次: {str(e)}") try: - if rs: - bd = js.call('main').split('=') - ck [bd[0]] = bd[1] - else: - cookie = {} - r = s.post('https://wapact.189.cn:9001/gateway/golden/api/lottery',json={"activityId":"6384b49b1e44396da4f1e4a3"},cookies=ck) - except: - pass - time.sleep(3) - + # 重试时重新获取 Ruishu cookies + ruishu_cookies = get_ruishu_cookies() + if not ruishu_cookies: + print("重试时无法获取 Ruishu cookies") + return None -def ks(phone, ticket, uid): - global wt - - wxp[phone] = uid - s = requests.session() - s.headers={"User-Agent":"Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36","Referer":"https://wapact.189.cn:9001/JinDouMall/JinDouMall_independentDetails.html"} - s.cookies.set_policy(BlockAll()) - s.mount('https://', DESAdapter()) - s.timeout = 30 - if rs: - bd = js.call('main').split('=') - ck [bd[0]] = bd[1] + paraV = encrypt_para(value) + response = session.post( + 'https://wapside.189.cn:9001/jt-sign/paradise/getLevelRightsList', + json={"para": paraV}, + cookies=ruishu_cookies + ) - - login = s.post('https://wapact.189.cn:9001/unified/user/login',json={"ticket":ticket,"backUrl":"https%3A%2F%2Fwapact.189.cn%3A9001","platformCode":"P201010301","loginType":2}, cookies=ck).json() - if login['code'] == 0: - printn(phone+" 获取token成功") - s.headers["Authorization"] = "Bearer " + login["biz"]["token"] + data = response.json() + if data.get('code') == 401: + print(f"重试获取失败:{data},原因大概是sign过期了") + return None - queryInfo(phone,s) - + current_level = int(data['currentLevel']) + key_name = 'V' + str(current_level) + ids = [item['id'] for item in data.get(key_name, []) if item.get('name') == '话费'] + return ids - if rs: - bd = js.call('main').split('=') - ck [bd[0]] = bd[1] - - queryBigDataAppGetOrInfo = s.get('https://wapact.189.cn:9001/gateway/golden/api/queryBigDataAppGetOrInfo?floorType=0&userType=1&page&1&order=2&tabOrder=',cookies=ck).json() - #printn(queryBigDataAppGetOrInfo) - for i in queryBigDataAppGetOrInfo["biz"]["ExchangeGoodslist"]: - if '话费' not in i["title"]:continue - - if '0.5元' in i["title"] or '5元' in i["title"]: - jp["9"][i["title"]] = i["id"] - elif '1元' in i["title"] or '10元' in i["title"]: - jp["13"][i["title"]] = i["id"] - else: - jp["12"][i["title"]] = i["id"] + except Exception as e: + print(f"重试也失败了: {str(e)}") + return None - +def get_ruishu_cookies(): + try: + # 获取当前文件所在目录 + current_dir = os.path.dirname(os.path.abspath(__file__)) + ruishu_path = os.path.join(current_dir, 'Ruishu.py') - h = datetime.datetime.now().hour - if 11 > h > 1: - h = 9 - - elif 23 > h > 1: - h = 13 - + # 执行 Ruishu.py 并获取输出 + result = subprocess.run([sys.executable, ruishu_path], + capture_output=True, + text=True) + + if result.returncode != 0: + print(f"Ruishu.py 执行错误: {result.stderr}") + return None + + # 解析输出的 JSON + cookies = json.loads(result.stdout.strip()) + return cookies + + except Exception as e: + print(f"获取 Ruishu cookies 时发生错误: {str(e)}") + return None + +async def getSign(ticket, session): + try: + # 获取 Ruishu cookies + ruishu_cookies = get_ruishu_cookies() + if not ruishu_cookies: + print("无法获取 Ruishu cookies") + return None + + # 合并现有的 cookies 和 Ruishu cookies + cookies = {**ruishu_cookies} + + # 使用合并后的 cookies 发送请求 + response = session.get( + 'https://wapside.189.cn:9001/jt-sign/ssoHomLogin?ticket=' + ticket, + cookies=cookies + ).json() + + if response.get('resoultCode') == '0': + sign = response.get('sign') + return sign else: - h = 23 - - if len(sys.argv) ==2: - h = int(sys.argv[1]) - #h=23 - d = jp[str(h)] - - wt = t(h) + kswt - - if jp["12"] != {}: - d.update(jp["12"]) - wt = 0 - - for di in d: - #if '5' in di: - if di not in dhjl[yf]: - dhjl[yf][di] = "" - if phone in dhjl[yf][di] : - printn(f"{phone} {di} 已兑换") - - else: + print(f"获取sign失败[{response.get('resoultCode')}]: {response}") + except Exception as e: + print(f"getSign 发生错误: {str(e)}") + return None - printn(f"{phone} {di}") - if wt - time.time() > 20 * 60: - print("等待时间超过20分钟") - return - - - threading.Thread(target=dh,args=(phone,s,di,d[di],wt, uid)).start() - - +async def qgNight(phone, ticket, timeDiff,isTrue): + if isTrue: + runTime = run_Time(23,59,3) else: - - printn(f"{phone} 获取token {login['message']}") - - + # runTime = run_Time(0,0,0) + 0.65 + runTime = 0 -def first_request(res=''): - global js, fw - url = 'https://wapact.189.cn:9001/gateway/stand/detail/exchange' - if res == '': - response = ss.get(url) - res = response.text - soup = BeautifulSoup(res, 'html.parser') - scripts = soup.find_all('script') - for script in scripts: - if 'src' in str(script): - rsurl = re.findall('src="([^"]+)"', str(script))[0] - - if '$_ts=window' in script.get_text(): - ts_code = script.get_text() - - - urls = url.split('/') - rsurl = urls[0] + '//' + urls[2] + rsurl - #print(rsurl) - ts_code += ss.get(rsurl).text - content_code = soup.find_all('meta')[1].get('content') - with open("ruishutongsha.js") as f: - js_code_ym = f.read() - js_code = js_code_ym.replace('content_code', content_code).replace("'ts_code'", ts_code) - js = execjs.compile(js_code) - - for cookie in ss.cookies: - ck[cookie.name] = cookie.value - return content_code, ts_code, ck - - - -def main(): - global wt,rs - r = ss.get('https://wapact.189.cn:9001/gateway/stand/detailNew/exchange') - if '$_ts=window' in r.text: - rs = 1 - print("瑞数加密已开启") - first_request() + if runTime >(time.time()+timeDiff): + difftime = runTime - time.time() - timeDiff + print(f"当前时间:{str(datetime.datetime.now())[11:23]},跟设定的时间不同,等待{difftime}秒开始兑换每天一次的") + await asyncio.sleep(difftime) + session = requests.Session() + session.mount('https://', DESAdapter()) + session.verify = False # 禁用证书验证 + sign =await getSign(ticket,session) + if sign: + # print(f"当前时间:{str(datetime.datetime.now())[11:23]}获取到了Sign:"+sign) + session.headers={"User-Agent":"Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36","sign":sign} else: - print("瑞数加密已关闭") - rs = 0 - if os.environ.get('jdhf')!= None: - chinaTelecomAccount = os.environ.get('jdhf') + print("未获取sign。") + return + rightsId =await getLevelRightsList(phone,session) + if rightsId: + print("获取到了rightsId:"+rightsId[0]) else: - chinaTelecomAccount = jdhf - - for i in chinaTelecomAccount.split('&'): - - i = i.split('@') - phone = i[0] - password = i[1] - uid = i[-1] - ticket = False - - #ticket = get_userTicket(phone) - - if phone in load_token: - printn(f'{phone} 使用缓存登录') - ticket = get_ticket(phone,load_token[phone]['userId'],load_token[phone]['token']) - - if ticket == False: - printn(f'{phone} 使用密码登录') - ticket = userLoginNormal(phone,password) - + print("未能获取rightsId。") + return + # await asyncio.sleep(10)直接延迟也行,或者用下面的等待一段时间。之所以这样是要先获取sign省一些步骤。 + if isTrue: + runTime2 = run_Time(23,59,59) + 0.7 + difftime = runTime2 - time.time() - timeDiff + printn(f"等待{difftime}s") + await asyncio.sleep(difftime) + await exchangeForDay(phone,session,run_num,rightsId[0],0.1) +async def qgDay(phone, ticket, timeDiff, isTrue): + async with AsyncSessionManager() as s: + pass +async def main(timeDiff,isTRUE,hour): + tasks = [] + PHONES=os.environ.get('chinaTelecomAccount') + phone_list = PHONES.split('\n') + for phoneV in phone_list: + value = phoneV.split('#') + phone, password = value[0], value[1] + printn(f'{get_first_three(phone)}开始登录') + ticket = userLoginNormal(phone,password) if ticket: - threading.Thread(target=ks,args=(phone, ticket, uid)).start() - - time.sleep(1) + # hour=datetime.datetime.now().hour + # hour=23 + if hour > 15: + tasks.append(qgNight(phone, ticket, timeDiff, isTRUE)) + # await asyncio.sleep(0.1) + else:#十点//十四点场次 + tasks.append(qgDay(phone, ticket, timeDiff, isTRUE)) + # await asyncio.sleep(0.1) else: printn(f'{phone} 登录失败') - + await asyncio.gather(*tasks) -jdhf = "" -cfcs = 5 -jdaid = '60dd79533dc03d3c76bdde30' -ck = {} -load_token_file = 'chinaTelecom_cache.json' -try: - with open(load_token_file, 'r') as f: - load_token = json.load(f) -except: - load_token = {} - -main() +if __name__ == "__main__": + h = datetime.datetime.now().hour +# h=15 #手动设置场次的时间 + print("当前小时为: "+str(h)) + if 10 >h >0: + print("当前小时为: "+str(h)+"已过0点但未到10点开始准备抢十点场次") + wttime= run_Time(9,59,8) #抢十点场次 + elif 14 >= h >=10: + print("当前小时为: "+str(h) +"已过10点但未到14点开始准备抢十四点场次") + wttime= run_Time(13,59,8) #抢十四点场次 + else: + print("当前小时为: "+str(h)+"已过14点开始准备抢凌晨") + wttime= run_Time(23,58,58) #抢凌晨 + # isTRUE=False + isTRUE=True + #isTRUE等于False则表示忽略所有限制直接运行。这个参数一般用于测试。实际生产一定要设置为True。 + if(wttime >time.time()) : + wTime=wttime-time.time() + print("未到时间,计算后差异:"+str(wTime)+"秒") + if isTRUE: + print("一定要先测试,根据自身 设定的重发和多号,不然会出问题,抢购过早或者过晚。") + print("开始等待:") + time.sleep(wTime) + # timeValue = getApiTime("https://f.m.suning.com/api/ct.do") + timeValue = 0 + timeDiff = timeValue if timeValue > 0 else 0 + asyncio.run(main(timeDiff, isTRUE,h)) + print("所有任务都已执行完毕!") diff --git a/ruishu.py b/ruishu.py new file mode 100644 index 0000000..4feb89c --- /dev/null +++ b/ruishu.py @@ -0,0 +1,272 @@ +import os +import ssl +import time +import json +import execjs +import base64 +import random +import certifi +import aiohttp +import asyncio +import requests +from http import cookiejar +from Crypto.Cipher import DES3 +from Crypto.Util.Padding import pad, unpad +from aiohttp import ClientSession, TCPConnector +import httpx + +diffValue = 2 +filename='Cache.js' +if os.path.exists(filename): + with open(filename, 'r', encoding='utf-8') as file: + fileContent = file.read() +else: + fileContent='' + +class BlockAll(cookiejar.CookiePolicy): + return_ok = set_ok = domain_return_ok = path_return_ok = lambda self, *args, **kwargs: False + netscape = True + rfc2965 = hide_cookie2 = False + +def printn(m): + print(f'\n{m}') + +context = ssl.create_default_context() +context.set_ciphers('DEFAULT@SECLEVEL=1') # 低安全级别0/1 +context.check_hostname = False # 禁用主机 +context.verify_mode = ssl.CERT_NONE # 禁用证书 + +class DESAdapter(requests.adapters.HTTPAdapter): + def init_poolmanager(self, *args, **kwargs): + kwargs['ssl_context'] = context + return super().init_poolmanager(*args, **kwargs) + +requests.DEFAULT_RETRIES = 0 +requests.packages.urllib3.disable_warnings() +ss = requests.session() +ss.headers = {"User-Agent": "Mozilla/5.0 (Linux; Android 13; 22081212C Build/TKQ1.220829.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36", "Referer": "https://wapact.189.cn:9001/JinDouMall/JinDouMall_independentDetails.html"} +ss.mount('https://', DESAdapter()) +ss.cookies.set_policy(BlockAll()) +runTime = 0 +sleepTime = 1 +key = b'1234567`90koiuyhgtfrdews' +iv = 8 * b'\0' + +def encrypt(text): + cipher = DES3.new(key, DES3.MODE_CBC, iv) + ciphertext = cipher.encrypt(pad(text.encode(), DES3.block_size)) + return ciphertext.hex() + +def decrypt(text): + ciphertext = bytes.fromhex(text) + cipher = DES3.new(key, DES3.MODE_CBC, iv) + plaintext = unpad(cipher.decrypt(ciphertext), DES3.block_size) + return plaintext.decode() + +ssl_context = ssl.create_default_context() +ssl_context.set_ciphers('DEFAULT@SECLEVEL=1') +ssl_context.check_hostname = False +ssl_context.verify_mode = ssl.CERT_NONE + +custom_client = httpx.Client( + verify=False, + http2=False, + transport=httpx.HTTPTransport( + retries=0, + verify=ssl_context + ) +) + +def initCookie(getUrl='https://wapact.189.cn:9001/gateway/standQuery/detailNew/exchange'): + global js_code_ym, fileContent + cookie = '' + response = custom_client.post(getUrl) + content = response.text.split(' content="')[2].split('" r=')[0] + code1 = response.text.split('$_ts=window')[1].split('