Collect seeded Qinglong script repositories
This commit is contained in:
376
脚本库/web版/账密/5_旅行相关/2025-11-20_5_travel_df64a760.py
Normal file
376
脚本库/web版/账密/5_旅行相关/2025-11-20_5_travel_df64a760.py
Normal file
@@ -0,0 +1,376 @@
|
||||
# Source: https://github.com/AkenClub/ken-iMoutai-Script/blob/main/5_travel.py
|
||||
# Raw: https://raw.githubusercontent.com/AkenClub/ken-iMoutai-Script/main/5_travel.py
|
||||
# Repo: AkenClub/ken-iMoutai-Script
|
||||
# Path: 5_travel.py
|
||||
# UploadedAt: 2025-11-20T09:54:34+08:00
|
||||
# SHA256: df64a760c6c34918f55f14d59e7fc1125fccaa7cef71b2e6d5f0fd9bab9f9e7b
|
||||
# Category: web版/账密
|
||||
# Evidence: web/H5关键词 + username/password/login
|
||||
|
||||
"""
|
||||
5、旅行 & 获取小茅运、首次分享奖励
|
||||
|
||||
通知:运行结果会调用青龙面板的通知渠道。
|
||||
|
||||
|
||||
配置环境变量:KEN_IMAOTAI_ENV
|
||||
-- 在旧版本青龙(例如 v2.13.8)中,使用 $ 作为分隔符时会出现解析环境变量失败,此时可以把 `$` 分隔符换为 `#` 作为分隔符。
|
||||
-- 📣 怕出错?**建议直接使用 `#` 作为分隔符即可** (2024-10-15 更新支持)。
|
||||
内容格式为:PHONE_NUMBER$USER_ID$DEVICE_ID$MT_VERSION$PRODUCT_ID_LIST$SHOP_ID^SHOP_MODE^PROVINCE^CITY$LAT$LNG$TOKEN$COOKIE
|
||||
解释:手机号码$用户ID$设备ID$版本号$商品ID列表$店铺ID店铺缺货时自动采用的模式^省份^城市$纬度$经度$TOKEN$COOKIE
|
||||
多个用户时使用 & 连接。
|
||||
|
||||
说明:^SHOP_MODE^PROVINCE^CITY 为可选
|
||||
|
||||
常量。
|
||||
- PHONE_NUMBER: 用户的手机号码。 --- 自己手机号码
|
||||
- CODE: 短信验证码。 --- 运行 1_generate_code.py 获取
|
||||
- DEVICE_ID: 设备的唯一标识符。 --- 运行 1_generate_code.py 获取
|
||||
- MT_VERSION: 应用程序的版本号。 --- 运行 1_generate_code.py 获取
|
||||
- USER_ID: 用户的唯一标识符。 --- 运行 2_login.py 获取
|
||||
- TOKEN: 用于身份验证的令牌。 --- 运行 2_login.py 获取
|
||||
- COOKIE: 用于会话管理的Cookie。 --- 运行 2_login.py 获取
|
||||
- PRODUCT_ID_LIST: 商品ID列表,表示用户想要预约的商品。--- 运行 3_retrieve_shop_and_product_info.py 获取
|
||||
- SHOP_ID: 店铺的唯一标识符。 --- 运行 3_retrieve_shop_and_product_info.py 获取
|
||||
可设置为 AUTO,则根据 SHOP_MODE 的值来选择店铺 ID。
|
||||
- SHOP_MODE:店铺缺货模式,可选值为NEAREST(距离最近)或INVENTORY(库存最多)。设置该值时,需要同时设置 PROVINCE 和 CITY。
|
||||
非必填,但 SHOP_ID 设置 AUTO 时为必填,需要同时设置 SHOP_MODE、PROVINCE 和 CITY。
|
||||
- PROVINCE: 用户所在的省份。 --- 与 3_retrieve_shop_and_product_info.py 填写的省份一致
|
||||
非必填,但 SHOP_MODE 设置为 NEAREST 或 INVENTORY 时为必填。
|
||||
- CITY: 用户所在的城市。 --- 与 3_retrieve_shop_and_product_info.py 填写的城市一致
|
||||
非必填,但 SHOP_MODE 设置为 NEAREST 或 INVENTORY 时为必填。
|
||||
- LAT: 用户所在位置的纬度。 --- 运行 3_retrieve_shop_and_product_info.py 获取
|
||||
- LNG: 用户所在位置的经度。 --- 运行 3_retrieve_shop_and_product_info.py 获取
|
||||
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import os
|
||||
import ast
|
||||
import io
|
||||
import re
|
||||
|
||||
from notify import send
|
||||
|
||||
# 每日 9:12 执行,可自行修改。旅行一个周期 30 天,最多获取 30 小茅运,每次旅行基本可获 1 ~ 3 个小茅运,所以一天一次旅行足矣。
|
||||
# 如需每日旅行多次,示例 12 9-20/4 * * * , 表示 9:12 到 20:12 期间每隔 4 小时执行一次,包括 9:12 和 20:12。
|
||||
# 比如 12 9,20 * * * 表示 9:12、20:12 执行。
|
||||
'''
|
||||
cron: 12 9 * * *
|
||||
new Env("5_旅行相关")
|
||||
'''
|
||||
|
||||
# 创建 StringIO 对象
|
||||
log_stream = io.StringIO()
|
||||
|
||||
# 配置 logging
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# 创建控制台 Handler
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setFormatter(
|
||||
logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
||||
|
||||
# 创建 StringIO Handler
|
||||
stream_handler = logging.StreamHandler(log_stream)
|
||||
# stream_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
||||
|
||||
# 将两个 Handler 添加到 logger
|
||||
logger.addHandler(console_handler)
|
||||
logger.addHandler(stream_handler)
|
||||
|
||||
# 读取 KEN_IMAOTAI_ENV 环境变量
|
||||
KEN_IMAOTAI_ENV = os.getenv('KEN_IMAOTAI_ENV', '')
|
||||
|
||||
# 解析 KEN_IMAOTAI_ENV 环境变量并保存到 user 列表
|
||||
users = []
|
||||
if KEN_IMAOTAI_ENV:
|
||||
env_list = KEN_IMAOTAI_ENV.split('&')
|
||||
for env in env_list:
|
||||
try:
|
||||
# 使用 re.split() 分割字符串,支持 '#' 和 '$'
|
||||
split_values = re.split(r'[#$]', env)
|
||||
|
||||
PHONE_NUMBER, USER_ID, DEVICE_ID, MT_VERSION, PRODUCT_ID_LIST, SHOP_ID, LAT, LNG, TOKEN, COOKIE = split_values
|
||||
|
||||
user = {
|
||||
'PHONE_NUMBER': PHONE_NUMBER.strip(),
|
||||
'USER_ID': USER_ID.strip(),
|
||||
'DEVICE_ID': DEVICE_ID.strip(),
|
||||
'MT_VERSION': MT_VERSION.strip(),
|
||||
'PRODUCT_ID_LIST': ast.literal_eval(PRODUCT_ID_LIST.strip()),
|
||||
'SHOP_ID': SHOP_ID.strip(),
|
||||
'LAT': LAT.strip(),
|
||||
'LNG': LNG.strip(),
|
||||
'TOKEN': TOKEN.strip(),
|
||||
'COOKIE': COOKIE.strip()
|
||||
}
|
||||
# 检查字段是否完整且有值
|
||||
required_fields = [
|
||||
'PHONE_NUMBER', 'USER_ID', 'DEVICE_ID', 'MT_VERSION',
|
||||
'PRODUCT_ID_LIST', 'SHOP_ID', 'LAT', 'LNG', 'TOKEN', 'COOKIE'
|
||||
]
|
||||
if all(user.get(field) for field in required_fields):
|
||||
# 判断 PRODUCT_ID_LIST 长度是否大于 0
|
||||
if len(user['PRODUCT_ID_LIST']) > 0:
|
||||
users.append(user)
|
||||
else:
|
||||
raise Exception("🚫 预约商品列表 - PRODUCT_ID_LIST 值为空,请添加后重试")
|
||||
else:
|
||||
logging.info(f"🚫 用户信息不完整: {user}")
|
||||
except Exception as e:
|
||||
logging.info(f"🚫 KEN_IMAOTAI_ENV 环境变量格式错误: {e}")
|
||||
|
||||
logging.info("找到以下用户配置:")
|
||||
# 输出用户信息
|
||||
for index, user in enumerate(users):
|
||||
logging.info(f"用户 {index + 1}: 📞 {user['PHONE_NUMBER']}")
|
||||
|
||||
else:
|
||||
logging.info("🚫 KEN_IMAOTAI_ENV 环境变量未定义")
|
||||
|
||||
base_url = "https://h5.moutai519.com.cn/game"
|
||||
|
||||
|
||||
# 生成请求头
|
||||
def generate_headers(device_id, mt_version, cookie, lat=None, lng=None):
|
||||
headers = {
|
||||
"MT-Device-ID": device_id,
|
||||
"MT-APP-Version": mt_version,
|
||||
"User-Agent": "iOS;16.3;Apple;?unrecognized?",
|
||||
"Cookie": f"MT-Token-Wap={cookie};MT-Device-ID-Wap={device_id};"
|
||||
}
|
||||
if lat and lng:
|
||||
headers["MT-Lat"] = lat
|
||||
headers["MT-Lng"] = lng
|
||||
return headers
|
||||
|
||||
|
||||
# 获得旅行奖励
|
||||
def travel_reward(device_id, mt_version, cookie, lat, lng):
|
||||
# 9-20点才能领取旅行奖励
|
||||
current_hour = datetime.now().hour
|
||||
if not (9 <= current_hour < 20):
|
||||
raise Exception("🚫 活动未开始,开始时间9点-20点")
|
||||
|
||||
page_data = get_user_isolation_page_data(device_id, mt_version, cookie)
|
||||
logging.info(f"【旅行前】用户数据:")
|
||||
|
||||
status = page_data.get("status")
|
||||
remain_chance = page_data.get("remainChance")
|
||||
energy_reward_value = page_data.get("energy_reward_value")
|
||||
energy = page_data.get("energy")
|
||||
end_time = page_data.get("end_time")
|
||||
|
||||
# 打印旅行前获得的用户数据
|
||||
log_travel_status(page_data)
|
||||
|
||||
# 如果存在未领取的耐力值奖励,则领取
|
||||
if energy_reward_value > 0:
|
||||
# 获取申购耐力值
|
||||
get_energy_award(cookie, device_id, mt_version, lat, lng)
|
||||
energy += energy_reward_value
|
||||
|
||||
# 本月剩余旅行奖励
|
||||
current_period_can_convert_xmy_num = get_exchange_rate_info(
|
||||
device_id, mt_version, cookie)
|
||||
if current_period_can_convert_xmy_num <= 0:
|
||||
raise Exception("🚫 当月无可领取奖励,直接结束旅行。")
|
||||
logging.info(f"📈当月可领取小茅运数量:{current_period_can_convert_xmy_num}")
|
||||
|
||||
# 进行中
|
||||
if status == 2:
|
||||
formatted_date = datetime.fromtimestamp(
|
||||
end_time / 1000).strftime("%Y-%m-%d %H:%M:%S")
|
||||
raise Exception(f"🚫 旅行暂未结束,本次旅行结束时间:{formatted_date}")
|
||||
# 已完成
|
||||
if status == 3:
|
||||
travel_reward_xmy = get_xm_travel_reward(device_id, mt_version, cookie)
|
||||
logging.info(f"🎁 本次旅行将奖励小茅运:{travel_reward_xmy}")
|
||||
|
||||
try:
|
||||
# 领取旅行获取的小茅运
|
||||
reward_result = receive_reward(device_id, lat, lng, cookie,
|
||||
mt_version)
|
||||
logging.info(f"🎁 领取小茅运结果:{reward_result}")
|
||||
except Exception as e:
|
||||
logging.error(f"🚫 领取小茅运失败: {e}")
|
||||
|
||||
try:
|
||||
# 首次分享获取耐力
|
||||
share_result = share_reward(device_id, lat, lng, cookie,
|
||||
mt_version)
|
||||
# 如果分享成功,则耐力值加 10,用于后续判断是否足够耐力值旅行
|
||||
energy += 10
|
||||
logging.info(f"🎁 分享奖励结果:{share_result}")
|
||||
except Exception as e:
|
||||
logging.error(f"🚫 分享奖励失败: {e}")
|
||||
|
||||
# 本次旅行奖励领取后, 当月实际剩余旅行奖励
|
||||
if travel_reward_xmy > current_period_can_convert_xmy_num:
|
||||
raise Exception("🚫 当月无可领取奖励,当月不再旅行")
|
||||
|
||||
# 如果是未开始状态或者 status 已完成且领取了奖励,则开始新的旅行
|
||||
if remain_chance < 1:
|
||||
raise Exception("🚫 当日旅行次数已耗尽")
|
||||
elif energy < 100:
|
||||
raise Exception(f"🚫 无法旅行,耐力不足100, 当前耐力值:{energy}")
|
||||
else:
|
||||
# 小茅运旅行活动
|
||||
start_travel(device_id, mt_version, cookie)
|
||||
|
||||
|
||||
def log_travel_status(page_data):
|
||||
status = page_data.get("status")
|
||||
remain_chance = page_data.get("remainChance")
|
||||
xmy = page_data.get("xmy")
|
||||
energy = page_data.get("energy")
|
||||
energy_reward_value = page_data.get("energy_reward_value")
|
||||
|
||||
logging.info(
|
||||
f"🌟当前旅行状态: {'未开始' if status == 1 else '进行中' if status == 2 else '已完成'}"
|
||||
)
|
||||
logging.info(f"📅当日剩余旅行次数: {remain_chance}")
|
||||
logging.info(f"💫小茅运: {xmy}")
|
||||
logging.info(f"💪耐力值: {energy}")
|
||||
logging.info(f"🎁未领取的耐力值奖励: {energy_reward_value}")
|
||||
|
||||
|
||||
# 领取旅行获取的小茅运
|
||||
def receive_reward(device_id, lat, lng, cookie, mt_version):
|
||||
url = f"{base_url}/xmTravel/receiveReward"
|
||||
headers = generate_headers(device_id, mt_version, cookie, lat, lng)
|
||||
response = requests.post(url, headers=headers)
|
||||
body = response.json()
|
||||
if body.get("code") != 2000:
|
||||
raise Exception(body)
|
||||
return body.get("data")
|
||||
|
||||
|
||||
# 领取每日首次分享获取耐力
|
||||
def share_reward(device_id, lat, lng, cookie, mt_version):
|
||||
url = f"{base_url}/xmTravel/shareReward"
|
||||
headers = generate_headers(device_id, mt_version, cookie, lat, lng)
|
||||
response = requests.post(url, headers=headers)
|
||||
body = response.json()
|
||||
if body.get("code") != 2000:
|
||||
raise Exception(body)
|
||||
return body.get("data")
|
||||
|
||||
|
||||
# 开始旅行
|
||||
def start_travel(device_id, mt_version, cookie):
|
||||
url = f"{base_url}/xmTravel/startTravel"
|
||||
headers = generate_headers(device_id, mt_version, cookie)
|
||||
response = requests.post(url, headers=headers)
|
||||
body = response.json()
|
||||
if body.get("code") != 2000:
|
||||
raise Exception(f"🚫 开始旅行失败:{body.get('message')}")
|
||||
start_travel_timestamp = body.get("data").get("startTravelTs", 0)
|
||||
start_travel_str = datetime.fromtimestamp(
|
||||
start_travel_timestamp / 1000).strftime("%Y-%m-%d %H:%M:%S")
|
||||
logging.info(f"✅ 开始旅行成功,旅行开始时间:{start_travel_str}")
|
||||
|
||||
|
||||
# 查询 可获取小茅运
|
||||
def get_xm_travel_reward(device_id, mt_version, cookie):
|
||||
url = f"{base_url}/xmTravel/getXmTravelReward"
|
||||
headers = generate_headers(device_id, mt_version, cookie)
|
||||
response = requests.get(url, headers=headers)
|
||||
body = response.json()
|
||||
if body.get("code") != 2000:
|
||||
raise Exception(f"🚫 {body.get('message')}")
|
||||
# 例如 1.95,可能会返回 None
|
||||
travel_reward_xmy = body.get("data").get("travelRewardXmy")
|
||||
return travel_reward_xmy if travel_reward_xmy is not None else 0
|
||||
|
||||
|
||||
# 获取用户数据,查询旅行状态、剩余可领取小茅运数量等
|
||||
def get_user_isolation_page_data(device_id, mt_version, cookie):
|
||||
url = f"{base_url}/isolationPage/getUserIsolationPageData"
|
||||
headers = generate_headers(device_id, mt_version, cookie)
|
||||
params = {"__timestamp": int(datetime.now().timestamp())}
|
||||
response = requests.get(url, headers=headers, params=params)
|
||||
body = response.json()
|
||||
if body.get("code") != 2000:
|
||||
raise Exception(f"🚫 获取用户数据 失败:{body.get('message')}")
|
||||
|
||||
data = body.get("data")
|
||||
# xmy: 小茅运值
|
||||
xmy = data.get("xmy")
|
||||
# energy: 耐力值
|
||||
energy = data.get("energy")
|
||||
xm_travel = data.get("xmTravel")
|
||||
energy_reward = data.get("energyReward")
|
||||
# status: 1. 未开始 2. 进行中 3. 已完成
|
||||
status = xm_travel.get("status")
|
||||
# travelEndTime: 旅行结束时间
|
||||
travel_end_time = xm_travel.get("travelEndTime")
|
||||
# remainChance 今日剩余旅行次数
|
||||
remain_chance = xm_travel.get("remainChance")
|
||||
# 可领取申购耐力值奖励
|
||||
energy_value = energy_reward.get("value")
|
||||
|
||||
end_time = travel_end_time * 1000
|
||||
|
||||
result = {
|
||||
"remainChance": remain_chance,
|
||||
"status": status,
|
||||
"xmy": xmy,
|
||||
"energy_reward_value": energy_value,
|
||||
"energy": energy,
|
||||
"end_time": end_time
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
# 获取申购耐力值
|
||||
def get_energy_award(cookie, device_id, mt_version, lat, lng):
|
||||
url = f"{base_url}/isolationPage/getUserEnergyAward"
|
||||
headers = generate_headers(device_id, mt_version, cookie, lat, lng)
|
||||
response = requests.post(url, headers=headers)
|
||||
body = response.text
|
||||
json_object = json.loads(body)
|
||||
if json_object.get("code") != 200:
|
||||
raise Exception(f"🚫 {json_object.get('message')}")
|
||||
return body
|
||||
|
||||
|
||||
# 获取本月剩余奖励耐力值
|
||||
def get_exchange_rate_info(device_id, mt_version, cookie):
|
||||
url = f"{base_url}/synthesize/exchangeRateInfo"
|
||||
headers = generate_headers(device_id, mt_version, cookie)
|
||||
params = {"__timestamp": int(datetime.now().timestamp())}
|
||||
response = requests.get(url, headers=headers, params=params)
|
||||
body = response.json()
|
||||
if body.get("code") != 2000:
|
||||
raise Exception(f"🚫 {body.get('message')}")
|
||||
# 返回本月剩余奖励耐力值
|
||||
return body.get("data").get("currentPeriodCanConvertXmyNum")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for user in users:
|
||||
logging.info('--------------------------')
|
||||
logging.info(f"🧾 用户:{user['PHONE_NUMBER']},执行旅行")
|
||||
try:
|
||||
travel_reward(user['DEVICE_ID'], user['MT_VERSION'],
|
||||
user['COOKIE'], user['LAT'], user['LNG'])
|
||||
except Exception as e:
|
||||
logging.error(f"🚫 旅行失败: {e}")
|
||||
finally:
|
||||
page_data = get_user_isolation_page_data(user['DEVICE_ID'],
|
||||
user['MT_VERSION'],
|
||||
user['COOKIE'])
|
||||
logging.info(f"【旅行后】用户数据:")
|
||||
log_travel_status(page_data)
|
||||
logging.info('--------------------------')
|
||||
|
||||
logging.info("✅ 所有用户旅行完成")
|
||||
|
||||
log_contents = log_stream.getvalue()
|
||||
send("i茅台旅行-日志:", log_contents)
|
||||
Reference in New Issue
Block a user