import threading
import yaml
import re
import qrcode
import wmi
import os
import time
import json
import requests
import base64
import hashlib
import hmac
import urllib.parse
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from datetime import datetime as dsa
# 初始化WMI和事件对象
w = wmi.WMI()
stop_event = threading.Event()
# AES CBC模式加密解密类
class My_AES_CBC:
def __init__(self, key, iv):
self.key = key
self.mode = AES.MODE_CBC
self.cryptor = AES.new(self.key, self.mode, iv)
def encrypt(self, plain_text):
encode_text = plain_text.encode('utf-8')
pad_text = pad(encode_text, AES.block_size)
encrypted_text = self.cryptor.encrypt(pad_text)
return encrypted_text
def decrypt(self, encrypted_text):
plain_text = self.cryptor.decrypt(encrypted_text)
plain_text = unpad(plain_text, AES.block_size).decode()
return plain_text
# 配置类,用于读取和验证配置文件
class Config:
def __init__(self, data):
self.cookie = data.get('cookie', None)
self.buy_time = data.get('buy_time', None)
self.request_time = data.get('request_time', None)
self.wait_time = data.get('wait_time', None)
self.sendkey = data.get('sendkey', None)
self.secret = data.get('secret', None)
self.access_token = data.get('access_token', None)
self.phone = data.get('phone', None)
self.ms = data.get('ms', None)
self.ip_proxy = data.get('ip_proxy', None)
self.tunnel_or_ydaili = data.get('tunnel_or_ydaili', None)
if not (self.cookie and self.buy_time and self.request_time and self.wait_time):
raise ValueError('参数存在空值, 请检测配置文件--config.yml---!')
# 读取配置文件
def read_config_file(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as file:
config_data = yaml.safe_load(file)
return Config(config_data)
except FileNotFoundError as e:
raise ValueError('未找到配置文件:config.yml')
# 加载配置和设置headers
conf = read_config_file('./config.yml')
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36',
'cookie': conf.cookie
}
# 获取淘宝服务器时间
def get_taobao_time():
try:
r1 = requests.get(url='http://acs.m.taobao.com/gw/mtop.common.getTimestamp/',
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.4098.3 Safari/537.36'})
print(r1.text)
x = eval(r1.text)
timeNum = int(x['data']['t'])
return float(timeNum / 1000)
except Exception as e:
print(f"获取淘宝时间出错: {e}")
return time.time() # 回退到使用本地时间
# 获取网络时间 - 使用淘宝时间API
def get_network_time():
try:
# 使用淘宝时间接口
taobao_time = get_taobao_time()
current_time = dsa.fromtimestamp(taobao_time)
print(f"获取到的时间: {current_time}")
return current_time
except Exception as e:
print(f"获取网络时间出错: {e}")
# 使用本地时间
local_time = dsa.now()
print(f"使用本地时间: {local_time}")
return local_time
# 获取商店ID
def get_shop_id(target_url):
data = requests.get(target_url, headers=headers)
links = re.findall('userid=(.*?)&', data.text)
shop_id = links[0]
return shop_id
# 获取商品ID和SKU ID
def get_itemid_skuid(target_url):
"""
:return:
item_id
item_sku_id
"""
ticket_url = target_url
item_id = ticket_url.split('itemID=')[1].split('&')[0]
url = 'https://thor.weidian.com/detail/getItemSkuInfo/1.0?param=%7B%22itemId%22%3A%22{}%22%7D'.format(item_id)
response = requests.get(url, headers=headers)
data = response.json()
try:
t = data['result']['skuInfos']
print('################################################################################')
for i in range(len(t)):
print('具体商品:{} skuid:{}'.format(t[i]['skuInfo']['title'], t[i]['skuInfo']['id']))
print('################################################################################')
sku_id = input('请输入待抢购商品的skuid:')
return (item_id, sku_id)
except:
return (item_id, 0)
# 获取用户地址
def get_address(item_id, item_sku_id):
"""
:param item_id:
:param item_sku_id:
:return:
获取用户地址
"""
param = {
'source_id': '4586c1e7d5c9165783ab23f82c97283f',
'channel': 'bjh5',
'item_list': [{
'extend': {},
'item_id': item_id,
'item_sku_id': item_sku_id,
'quantity': 1,
'price_type': 1
}],
'buyer': {}
}
url = 'https://thor.weidian.com/vbuy/ConfirmOrder/1.0?'
url = url + 'param=' + str(param)
response = requests.post(url, headers=headers)
result = response.json()
status = result['status']['message']
if status == 'LOGIN ERROR':
print('账号cookie过期,请重新填写')
address_id = result['result']['buyer_address']['address_id']
return address_id
# 准备抢票所需的参数
def question_list(target_url):
item_id, item_sku_id = get_itemid_skuid(target_url)
address_id = get_address(item_id, item_sku_id)
shop_id = get_shop_id(target_url)
origin_price = float(input('请输入购买商品单价:'))
ticket_num = int(float(input('请输入购买商品数量:')))
express_price = float(input('请输入购买所需运费价格:'))
question = []
# 配置参数1
config_param = {
'channel': 'bjh5',
'source_id': '33287e7b22a49a1012f04cfcc658bd41',
'q_pv_id': '08d40000018b908f1a430a2075ad2a46',
'biz_type': 1,
'buyer': {
'buyer_id': '1450232039',
'eat_in_table_name': '',
'address_id': address_id
},
'shop_list': [{
'shop_id': shop_id,
'f_shop_id': '',
'sup_id': '',
'item_list': [{
'item_id': item_id,
'quantity': ticket_num,
'item_sku_id': item_sku_id,
'ori_price': origin_price,
'price': origin_price,
'extend': {},
'price_type': 1,
'discount_list': [],
'item_convey_info': {}
}],
'order_type': 3,
'ori_price': origin_price * ticket_num,
'price': origin_price * ticket_num + express_price,
'express_fee': express_price,
'express_type': 4,
'discount_list': [],
'invalid_item_list': []
}],
'deliver_type': 0,
'is_no_ship_addr': 0,
'total_pay_price': origin_price * ticket_num + express_price,
'total_vjifen': '',
'wfr': 'wxBuyerShare',
'discount_list': [],
'invalid_shop_list': [],
'pay_type': 0
}
question.append(config_param)
return question
# 使用Server酱发送微信消息
def get_weimessage(sendkey, phone):
api = 'https://sc.ftqq.com/{}.send'.format(sendkey)
title = phone + '票星球抢票成功'
content = '\n 请尽快在五分钟内付款\n '
data = {'text': title, 'desp': content}
req = requests.post(api, data=data)
print(req.text)
# 使用钉钉发送消息
def dingMessage(phone):
timestamp = str(round(time.time() * 1000))
secret = conf.secret
secret_enc = secret.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, secret)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
webhook = f'https://oapi.dingtalk.com/robot/send?access_token={conf.access_token}×tamp={timestamp}&sign={sign}'
header = {'Content-Type': 'application/json', 'Charset': 'UTF-8'}
tex = '手机号:{}---票星球抢购成功'.format(phone)
message = {
'msgtype': 'text',
'text': {'content': f'{tex}'},
'at': {'atMobiles': ['137...7091'], 'isAtAll': False}
}
message_json = json.dumps(message)
info = requests.post(url=webhook, data=message_json, headers=header)
print(info.text)
# 从API获取代理
def get_proxies_from_api(api_url):
response = requests.get(api_url)
data = response.text
return data
# 执行抢票任务
def task_to_run(url, lock, result_tracker, count, found, stop_event):
print("===== 抢票任务开始执行 =====")
api_url = conf.ip_proxy
max_retries = 50 # 最大重试次数
retry_count = 0
while not stop_event.is_set() and retry_count < max_retries:
retry_count += 1
print(f"抢票尝试 #{retry_count}/{max_retries}")
try:
if conf.tunnel_or_ydaili == 'ydaili':
proxy = get_proxies_from_api(api_url)
proxies = {
'http': '{}'.format(proxy.replace('\r\n', '')),
'https': '{}'.format(proxy.replace('\r\n', ''))
}
response = requests.post(url, headers=headers, proxies=proxies).json()
else:
response = requests.post(url, headers=headers).json()
print(f"抢票响应: {response}")
code = response['status']['code']
if code == 0:
with lock:
result_tracker[code] = result_tracker.get(code, 0) + 1
if result_tracker[code] == count:
print('抢票成功,请抓紧时间付款')
try:
get_weimessage(conf.sendkey, conf.phone)
dingMessage(conf.phone)
except Exception as e:
print(f"发送通知失败: {e}")
found.set()
return
else:
if conf.tunnel_or_ydaili == 'ydaili':
proxy = get_proxies_from_api(api_url)
proxies = {
'http': '{}'.format(proxy.replace('\r\n', '')),
'https': '{}'.format(proxy.replace('\r\n', ''))
}
response = requests.post(url, headers=headers, proxies=proxies).json()
else:
response = requests.post(url, headers=headers).json()
time.sleep(conf.request_time)
if hasattr(response, 'text') and '人潮拥挤' not in response.text:
print(response.text)
except Exception as e:
print(f"抢票过程中出错: {e}")
time.sleep(1)
if stop_event.is_set():
break
# 如果达到最大重试次数,通知主线程结束等待
print(f"达到最大尝试次数: {max_retries},任务结束")
found.set()
# 准备并发送抢票请求
def post_data(q_list, index, found, stop_event, count, lock, result_tracker):
try:
param = q_list[index]
base_url = 'https://thor.weidian.com/vbuy/CreateOrder/1.0?'
url = base_url + 'param=' + str(param)
print(f"线程 #{index} 准备中...")
# 解析抢票时间
current_time = get_network_time()
print(f"当前时间: {current_time}")
try:
buy_time_parts = conf.buy_time.split(' ')
print(f"抢票时间设定: {conf.buy_time}")
buy_time = dsa(
int(buy_time_parts[0]),
int(buy_time_parts[1]),
int(buy_time_parts[2]),
int(buy_time_parts[3]),
int(buy_time_parts[4]),
int(buy_time_parts[5])
)
print(f"转换后的抢票时间: {buy_time}")
# 计算等待时间
remaining_seconds = (buy_time - current_time).total_seconds()
print(f"原始剩余等待时间: {remaining_seconds}秒")
# 处理时间已过的情况
if remaining_seconds < 0:
print(f"警告: 设定的抢票时间 {buy_time} 已过期,立即开始抢票!")
remaining_seconds = 0
# 直接执行抢票,不使用Timer
if remaining_seconds <= 0:
print("立即开始抢票")
task_to_run(url, lock, result_tracker, count, found, stop_event)
else:
# 使用Timer延迟执行
print(f"倒计时 {remaining_seconds:.2f} 秒后将执行任务...")
timer = threading.Timer(
remaining_seconds,
task_to_run,
args=(url, lock, result_tracker, count, found, stop_event)
)
timer.daemon = True # 设置为守护线程,主线程结束时自动退出
timer.start()
print(f"定时器已启动,线程ID: {timer.ident}")
except Exception as e:
print(f"解析时间失败: {e}")
# 时间解析失败,直接开始抢票
print("时间解析出错,立即开始抢票")
task_to_run(url, lock, result_tracker, count, found, stop_event)
except Exception as e:
print(f"准备抢票任务时出错: {e}")
# 创建二维码
def create_qr_code(data, file_name):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save(file_name)
# 获取支付链接并生成二维码
def get_pay_links():
try:
data = requests.get('https://weidian.com/user/order/list.php?type=1&spider_token=bfe3&wfr=c&ifr=itemdetail',
headers=headers)
links = re.findall('找人代付(.*?);\\}', data.text)
cp_re = []
for link in links:
s = re.findall('https://[^\\s]+', link)
if s:
result = s[0].replace('amp;', '')
cp_re.append(result)
if not cp_re:
print("没有找到支付链接")
return
if not os.path.exists('./buy_link'):
os.makedirs('./buy_link')
for i, data in enumerate(cp_re):
create_qr_code(data, './buy_link/{}.jpg'.format(i + 1))
print(f"已生成支付二维码: ./buy_link/{i + 1}.jpg")
except Exception as e:
print(f"获取支付链接出错: {e}")
# 主函数
def main():
try:
found = threading.Event()
stop_event = threading.Event()
lock = threading.Lock()
result_tracker = {}
count = 0
# 测试获取淘宝服务器时间
taobao_time = get_taobao_time()
target_url = input('请输入待抢购的链接:')
q_list = question_list(target_url)
count += 1
while True:
confirm = input('是否还有待加入购物车的链接,如果有,请,输入 yes ,否则输入 no : ')
if confirm.lower() == 'yes':
target_url = input('请输入待抢购的链接:')
config_params = question_list(target_url)
q_list = q_list + config_params
count += 1
else:
break
threads = []
for i in range(len(q_list)):
thread = threading.Thread(
target=post_data,
args=(q_list, i, found, stop_event, count, lock, result_tracker)
)
thread.daemon = True # 设置为守护线程
threads.append(thread)
thread.start()
print(f"线程 #{i} 已启动")
# 设置一个合理的超时时间,防止无限等待
max_wait_time = 3600 # 最大等待1小时
print(f"主线程等待抢票结果,最多等待 {max_wait_time} 秒")
found.wait(timeout=max_wait_time)
print("停止所有抢票任务")
stop_event.set()
# 给线程一些时间优雅退出
for thread in threads:
thread.join(timeout=5)
print("抢票程序执行完毕")
except Exception as e:
print(f"主程序运行出错: {e}")
# 程序入口
if __name__ == '__main__':
try:
# 直接运行主程序,跳过授权验证
main()
confirm = input('是否需要生成付款二维码,需要,请,输入 yes ,否则输入 no : ')
if confirm.lower() == 'yes':
get_pay_links()
except Exception as e:
print(f"程序运行出错: {e}")
input("按Enter键退出...") # 让程序在出错时不立即关闭
帮我添加一个界面操作模式