123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- import base64
- import datetime
- import json
- import math
- import os
- import random
- import time
- import requests
- from Crypto.Cipher import AES
- from dailycheckin import CheckIn
- class Encrypt:
- def __init__(self, key, iv):
- self.key = key.encode("utf-8")
- self.iv = iv.encode("utf-8")
- def pkcs7padding(self, text):
- """明文使用PKCS7填充"""
- bs = 16
- length = len(text)
- bytes_length = len(text.encode("utf-8"))
- padding_size = length if (bytes_length == length) else bytes_length
- padding = bs - padding_size % bs
- padding_text = chr(padding) * padding
- self.coding = chr(padding)
- return text + padding_text
- def aes_encrypt(self, content):
- """AES加密"""
- cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
- content_padding = self.pkcs7padding(content)
- encrypt_bytes = cipher.encrypt(content_padding.encode("utf-8"))
- result = str(base64.b64encode(encrypt_bytes), encoding="utf-8")
- return result
- def aes_decrypt(self, content):
- """AES解密"""
- cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
- content = base64.b64decode(content)
- text = cipher.decrypt(content).decode("utf-8")
- return text.rstrip(self.coding)
- class IMAOTAI(CheckIn):
- name = "i茅台"
- def __init__(self, check_item):
- self.check_item = check_item
- self.RESERVE_RULE = 0
- self.mt_r = "clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/"
- self.ITEM_MAP = {
- "10941": "53%vol 500ml贵州茅台酒(甲辰龙年)",
- "10942": "53%vol 375ml×2贵州茅台酒(甲辰龙年)",
- "10056": "53%vol 500ml茅台1935",
- "2478": "53%vol 500ml贵州茅台酒(珍品)",
- }
- self.ITEM_CODES = ["10941", "10942"]
- AES_KEY = "qbhajinldepmucsonaaaccgypwuvcjaa"
- AES_IV = "2018534749963515"
- self.encrypt = Encrypt(key=AES_KEY, iv=AES_IV)
- self.mt_version = json.loads(
- requests.get("https://itunes.apple.com/cn/lookup?id=1600482450").text
- )["results"][0]["version"]
- self.headers = {}
- self.header_context = """
- MT-Lat: 28.499562
- MT-K: 1675213490331
- MT-Lng: 102.182324
- Host: app.moutai519.com.cn
- MT-User-Tag: 0
- Accept: */*
- MT-Network-Type: WIFI
- MT-Token: 1
- MT-Info: 028e7f96f6369cafe1d105579c5b9377
- MT-Device-ID: 2F2075D0-B66C-4287-A903-DBFF6358342A
- MT-Bundle-ID: com.moutai.mall
- Accept-Language: en-CN;q=1, zh-Hans-CN;q=0.9
- MT-Request-ID: 167560018873318465
- MT-APP-Version: 1.3.7
- User-Agent: iOS;16.3;Apple;?unrecognized?
- MT-R: clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==
- Content-Length: 93
- Accept-Encoding: gzip, deflate, br
- Connection: keep-alive
- Content-Type: application/json
- userId: 2
- """
- def init_headers(
- self,
- user_id: str = "1",
- token: str = "2",
- lat: str = "29.83826",
- lng: str = "119.74375",
- ):
- for k in self.header_context.strip().split("\n"):
- temp_l = k.split(": ")
- dict.update(self.headers, {temp_l[0]: temp_l[1]})
- dict.update(self.headers, {"userId": user_id})
- dict.update(self.headers, {"MT-Token": token})
- dict.update(self.headers, {"MT-Lat": lat})
- dict.update(self.headers, {"MT-Lng": lng})
- dict.update(self.headers, {"MT-APP-Version": self.mt_version})
- def get_current_session_id(self):
- day_time = int(time.mktime(datetime.date.today().timetuple())) * 1000
- my_url = f"https://static.moutai519.com.cn/mt-backend/xhr/front/mall/index/session/get/{day_time}"
- responses = requests.get(my_url)
- if responses.status_code != 200:
- print(
- f"get_current_session_id : params : {day_time}, response code : {responses.status_code}, response body : {responses.text}"
- )
- current_session_id = responses.json()["data"]["sessionId"]
- dict.update(self.headers, {"current_session_id": str(current_session_id)})
- def get_map(self, lat: str = "28.499562", lng: str = "102.182324"):
- p_c_map = {}
- url = "https://static.moutai519.com.cn/mt-backend/xhr/front/mall/resource/get"
- headers = {
- "X-Requested-With": "XMLHttpRequest",
- "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0_1 like Mac OS X)",
- "Referer": "https://h5.moutai519.com.cn/gux/game/main?appConfig=2_1_2",
- "Client-User-Agent": "iOS;16.0.1;Apple;iPhone 14 ProMax",
- "MT-R": "clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==",
- "Origin": "https://h5.moutai519.com.cn",
- "MT-APP-Version": self.mt_version,
- "MT-Request-ID": f"{int(time.time() * 1000)}{random.randint(1111111, 999999999)}{int(time.time() * 1000)}",
- "Accept-Language": "zh-CN,zh-Hans;q=1",
- "MT-Device-ID": f"{int(time.time() * 1000)}{random.randint(1111111, 999999999)}{int(time.time() * 1000)}",
- "Accept": "application/json, text/javascript, */*; q=0.01",
- "mt-lng": f"{lng}",
- "mt-lat": f"{lat}",
- }
- res = requests.get(url, headers=headers)
- mtshops = res.json().get("data", {}).get("mtshops_pc", {})
- urls = mtshops.get("url")
- r = requests.get(urls)
- for k, v in dict(r.json()).items():
- provinceName = v.get("provinceName")
- cityName = v.get("cityName")
- if not p_c_map.get(provinceName):
- p_c_map[provinceName] = {}
- if not p_c_map[provinceName].get(cityName, None):
- p_c_map[provinceName][cityName] = [k]
- else:
- p_c_map[provinceName][cityName].append(k)
- return p_c_map, dict(r.json())
- def max_shop(self, city, item_code, p_c_map, province, shops):
- max_count = 0
- max_shop_id = "0"
- shop_ids = p_c_map[province][city]
- for shop in shops:
- shopId = shop["shopId"]
- items = shop["items"]
- if shopId not in shop_ids:
- continue
- for item in items:
- if item["itemId"] != str(item_code):
- continue
- if item["inventory"] > max_count:
- max_count = item["inventory"]
- max_shop_id = shopId
- print(
- f"item code {item_code}, max shop id : {max_shop_id}, max count : {max_count}"
- )
- return max_shop_id
- def distance_shop(
- self,
- item_code,
- shops,
- source_data,
- lat: str = "28.499562",
- lng: str = "102.182324",
- ):
- temp_list = []
- for shop in shops:
- shopId = shop["shopId"]
- items = shop["items"]
- item_ids = [i["itemId"] for i in items]
- if str(item_code) not in item_ids:
- continue
- shop_info = source_data.get(shopId)
- d = math.sqrt(
- (float(lat) - shop_info["lat"]) ** 2
- + (float(lng) - shop_info["lng"]) ** 2
- )
- temp_list.append((d, shopId))
- temp_list = sorted(temp_list, key=lambda x: x[0])
- if len(temp_list) > 0:
- return temp_list[0][1]
- else:
- return "0"
- def get_location_count(
- self,
- province: str,
- city: str,
- item_code: str,
- p_c_map: dict,
- source_data: dict,
- lat: str = "29.83826",
- lng: str = "102.182324",
- reserve_rule: int = 0,
- ):
- day_time = int(time.mktime(datetime.date.today().timetuple())) * 1000
- session_id = self.headers["current_session_id"]
- responses = requests.get(
- f"https://static.moutai519.com.cn/mt-backend/xhr/front/mall/shop/list/slim/v3/{session_id}/{province}/{item_code}/{day_time}"
- )
- if responses.status_code != 200:
- print(
- f"get_location_count : params : {day_time}, response code : {responses.status_code}, response body : {responses.text}"
- )
- shops = responses.json()["data"]["shops"]
- if reserve_rule == 0:
- return self.distance_shop(item_code, shops, source_data, lat, lng)
- if reserve_rule == 1:
- return self.max_shop(city, item_code, p_c_map, province, shops)
- def act_params(self, shop_id: str, item_id: str):
- session_id = self.headers["current_session_id"]
- userId = self.headers["userId"]
- params = {
- "itemInfoList": [{"count": 1, "itemId": item_id}],
- "sessionId": int(session_id),
- "userId": userId,
- "shopId": shop_id,
- }
- s = json.dumps(params)
- act = self.encrypt.aes_encrypt(s)
- params.update({"actParam": act})
- return params
- def reservation(self, params: dict):
- params.pop("userId")
- responses = requests.post(
- "https://app.moutai519.com.cn/xhr/front/mall/reservation/add",
- json=params,
- headers=self.headers,
- ).json()
- if responses.get("code") == 401:
- msg = {
- "name": "申购结果",
- "value": "token失效, 请重新抓包获取",
- }
- elif responses.get("code") != 2000:
- msg = {
- "name": "申购结果",
- "value": responses.get("message"),
- }
- else:
- msg = {
- "name": "申购结果",
- "value": responses.get("data", {}).get("successDesc"),
- }
- return msg
- def getUserEnergyAward(self):
- """
- 耐力值
- """
- cookies = {
- "MT-Device-ID-Wap": self.headers["MT-Device-ID"],
- "MT-Token-Wap": self.headers["MT-Token"],
- "YX_SUPPORT_WEBP": "1",
- }
- response = requests.post(
- url="https://h5.moutai519.com.cn/game/isolationPage/getUserEnergyAward",
- cookies=cookies,
- headers=self.headers,
- json={},
- ).json()
- if response.get("code") == 200:
- msg = {
- "name": "耐力",
- "value": "✅领取耐力成功",
- }
- else:
- msg = {
- "name": "耐力",
- "value": response.get("message"),
- }
- return msg
- def main(self):
- msg = []
- mobile = self.check_item.get("mobile")
- province = self.check_item.get("province")
- city = self.check_item.get("city")
- token = self.check_item.get("token")
- userId = self.check_item.get("userid")
- lat = self.check_item.get("lat")
- lng = self.check_item.get("lng")
- item_codes = self.check_item.get("item_codes", self.ITEM_CODES)
- reserve_rule = self.check_item.get("reserve_rule", 0)
- msg = [
- {
- "name": "手机号",
- "value": f"{mobile}",
- },
- {
- "name": "省份城市",
- "value": f"{province}{city}",
- },
- ]
- p_c_map, source_data = self.get_map(lat=lat, lng=lng)
- self.get_current_session_id()
- self.init_headers(user_id=userId, token=token, lng=lng, lat=lat)
- try:
- for item in item_codes:
- max_shop_id = self.get_location_count(
- province=province,
- city=city,
- item_code=item,
- p_c_map=p_c_map,
- source_data=source_data,
- lat=lat,
- lng=lng,
- reserve_rule=reserve_rule,
- )
- if max_shop_id == "0":
- continue
- reservation_params = self.act_params(max_shop_id, item)
- reservation_msg = self.reservation(reservation_params)
- time.sleep(20)
- award_msg = self.getUserEnergyAward()
- msg.append(reservation_msg)
- msg.append(award_msg)
- except BaseException as e:
- msg.append(
- {
- "name": "申购结果",
- "value": e,
- }
- )
- msg = "\n".join([f"{one.get('name')}: {one.get('value')}" for one in msg])
- return msg
- if __name__ == "__main__":
- with open(
- os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json"),
- encoding="utf-8",
- ) as f:
- datas = json.loads(f.read())
- _check_item = datas.get("IMAOTAI", [])[0]
- print(IMAOTAI(check_item=_check_item).main())
|