添加 WX_Applet/Applet_WLJi.py
This commit is contained in:
717
WX_Applet/Applet_WLJi.py
Normal file
717
WX_Applet/Applet_WLJi.py
Normal file
@@ -0,0 +1,717 @@
|
||||
# cron: 12 8 * * *
|
||||
# new Env("王老吉瓶盖")
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
青龙面板微信协议自动化脚本 - 王老吉扫码抽奖
|
||||
账号从环境变量 WLJ_ID 获取,格式:备注#wxid#手机号,多行分割
|
||||
码字从同目录 WLJ_MZ.txt 文件中获取,每行一个码,用完自动删除
|
||||
抽奖参数从环境变量 WLJ_VT 获取,格式:上限#最大用码#最小间隔#最大间隔#成本
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import io
|
||||
import time
|
||||
import json
|
||||
import random
|
||||
import hashlib
|
||||
import requests
|
||||
import logging
|
||||
from typing import Optional, List, Dict
|
||||
from urllib.parse import urlencode
|
||||
|
||||
# ==================== 养鸡场配置 ====================
|
||||
WX_CLOUD = os.getenv('wx_cloud', '')
|
||||
AUTH_TOKEN = os.getenv('wx_token', '')
|
||||
|
||||
# ==================== 代理配置 ====================
|
||||
PROXY_DEFAULT = ""
|
||||
PROXY_API_URL = ""
|
||||
USE_PROXY = True
|
||||
|
||||
# ==================== 抽奖参数(从环境变量 WLJ_VT 整合) ====================
|
||||
WLJ_VT_STR = os.getenv('WLJ_VT', '2#5#5#10#0.34')
|
||||
try:
|
||||
parts = WLJ_VT_STR.split('#')
|
||||
DAILY_LOTTERY_LIMIT = int(parts[0])
|
||||
MAX_CODE_ATTEMPTS = int(parts[1])
|
||||
LOTTERY_INTERVAL_MIN = float(parts[2])
|
||||
LOTTERY_INTERVAL_MAX = float(parts[3])
|
||||
LOTTERY_CODE_PRICE = float(parts[4])
|
||||
except:
|
||||
DAILY_LOTTERY_LIMIT = 2
|
||||
MAX_CODE_ATTEMPTS = 5
|
||||
LOTTERY_INTERVAL_MIN = 5
|
||||
LOTTERY_INTERVAL_MAX = 10
|
||||
LOTTERY_CODE_PRICE = 0.34
|
||||
|
||||
# ==================== 账号配置 ====================
|
||||
WX_LIST_MANUAL = os.getenv("WLJ_ID", "")
|
||||
|
||||
REMOVE_WXIDS = [
|
||||
"wxid_x4nz2s4th45k22",
|
||||
"wxid_fog306otfw9q22",
|
||||
"wxid_tso9447iuq0t22",
|
||||
"wxid_5jtncnh3v8ud2",
|
||||
]
|
||||
|
||||
DELAY_BETWEEN_ACCOUNTS = 3
|
||||
WX_APPID = "wxd25dc8ba975776e3"
|
||||
|
||||
# ==================== API配置 ====================
|
||||
WLJ_BASE_URL = "https://wechatec.brand.wljhealth.com"
|
||||
S3_BASE_URL = "https://s3.lsa0.cn"
|
||||
|
||||
POSSESSOR = "50c7d6a87202429a9871bf61ec85ad99"
|
||||
MALL_CODE = "wxd25dc8ba975776e3"
|
||||
SALE_CHANNEL = "mall"
|
||||
|
||||
# ==================== 位置配置 ====================
|
||||
SCAN_LOCATIONS = [
|
||||
("28.228521", "112.939423"),
|
||||
("28.364827", "112.815102"),
|
||||
("28.157439", "113.632571"),
|
||||
("28.261334", "112.548920"),
|
||||
("27.832155", "113.158607"),
|
||||
("27.672908", "113.497244"),
|
||||
("27.869672", "112.912388"),
|
||||
("27.538421", "112.287695"),
|
||||
("27.751183", "112.503816"),
|
||||
("28.358012", "112.196543"),
|
||||
("28.498775", "112.221098"),
|
||||
("28.487346", "113.032817"),
|
||||
("28.683512", "112.869411"),
|
||||
("27.438907", "111.594236"),
|
||||
]
|
||||
|
||||
# ==================== 码字文件路径 ====================
|
||||
CODE_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "WLJ_MZ.txt")
|
||||
|
||||
# ==================== 调试模式 ====================
|
||||
DEBUG_MODE = False
|
||||
|
||||
# ==================== User-Agent 轮换池 ====================
|
||||
USER_AGENTS = [
|
||||
"Mozilla/5.0 (Linux; Android 14; HUAWEI Mate 60 RS Build/Mate60RS; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/935 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Honor Magic V2 Build/MagicV2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/870 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Xiaomi 13 Build/13; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/882 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Xiaomi Mix Fold 3 Build/MixFold3; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/927 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; HUAWEI Mate 70 Pro Build/Mate70Pro; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/916 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; HUAWEI Mate 70 Pro Build/Mate70Pro; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/914 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; OnePlus Ace 3 Pro Build/Ace3Pro; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/905 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; OnePlus 11 Build/11; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/859 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; OPPO Find N3 Flip Build/FindN3Flip; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/928 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Xiaomi 14 Pro Build/14Pro; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/953 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; HUAWEI Pura 70 Build/Pura70; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/958 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Honor Magic5 Build/Magic5; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/846 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; OPPO Find X7 Ultra Build/FindX7Ultra; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/844 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; HUAWEI Mate 70 Ultra Build/Mate70Ultra; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/823 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Xiaomi Mix Flip Build/MixFlip; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/808 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; HUAWEI P60 Pro Build/P60Pro; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/835 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Honor Magic6 Build/Magic6; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/845 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Honor Magic5 Build/Magic5; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/812 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Honor Magic6 RSR Build/Magic6RSR; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/926 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; OnePlus Ace 3 Build/Ace3; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/806 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; vivo X90 Pro+ Build/X90ProPlus; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/912 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; OPPO Find X7 Ultra Build/FindX7Ultra; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/886 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; HUAWEI Pura 70 Pro Build/Pura70Pro; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/831 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; vivo Fold 3 Build/Fold3; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/949 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; HUAWEI Pura 80 Pro Build/Pura80Pro; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/848 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; vivo Fold 3 Build/Fold3; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/901 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Xiaomi Redmi K70 Build/RedmiK70; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/955 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; OnePlus Ace 3 Build/Ace3; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/818 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Xiaomi Mix Fold 3 Build/MixFold3; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/805 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; OPPO Find X7 Ultra Build/FindX7Ultra; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/875 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; Xiaomi Mix Flip Build/MixFlip; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/950 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
"Mozilla/5.0 (Linux; Android 14; OPPO Find X7 Ultra Build/FindX7Ultra; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420273 MMWEBSDK/20250201 MMWEBID/883 MicroMessenger/8.0.60.2860(0x28003C55) WeChat/arm64 Weixin NetType/5G Language/zh_CN ABI/arm64 MiniProgramEnv/android",
|
||||
]
|
||||
|
||||
# ==================== 设备型号 ====================
|
||||
DEVICE_MODELS = [
|
||||
"Xiaomi-MixFold3",
|
||||
"Huawei-Mate60RS",
|
||||
"Huawei-P60",
|
||||
"Honor-Magic6RSR",
|
||||
"Honor-Magic5",
|
||||
"Huawei-Nova12Pro",
|
||||
"Xiaomi-RedmiK70Pro",
|
||||
"Huawei-Pura70",
|
||||
"Huawei-Pura70Pro",
|
||||
"OPPO-Reno11",
|
||||
"OnePlus-Ace3Pro",
|
||||
"Huawei-Nova12",
|
||||
"OPPO-FindX7Ultra",
|
||||
"vivo-X90ProPlus",
|
||||
"Huawei-Mate70Pro+",
|
||||
"Huawei-MateX3",
|
||||
"Xiaomi-14Ultra",
|
||||
"Huawei-Mate70RS",
|
||||
"OPPO-Reno11Pro",
|
||||
"vivo-X100Ultra",
|
||||
"OPPO-FindN3Flip",
|
||||
"OnePlus-Ace3",
|
||||
"Huawei-Pura80",
|
||||
"Huawei-Pura80Pro",
|
||||
"Huawei-Mate60Pro+",
|
||||
"Huawei-P60Pro+",
|
||||
"Huawei-MateX5",
|
||||
"Xiaomi-13",
|
||||
"Xiaomi-MixFlip",
|
||||
"Xiaomi-RedmiK60Pro",
|
||||
"vivo-X100",
|
||||
"Xiaomi-RedmiK60",
|
||||
]
|
||||
|
||||
# -------------------- 日志配置 --------------------
|
||||
class SimpleFormatter(logging.Formatter):
|
||||
def format(self, record):
|
||||
return record.getMessage()
|
||||
|
||||
handler = logging.StreamHandler(io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8"))
|
||||
handler.setFormatter(SimpleFormatter())
|
||||
logging.basicConfig(level=logging.INFO, handlers=[handler])
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WangLaoJiAutomation:
|
||||
def __init__(self):
|
||||
self.session = requests.Session()
|
||||
self.wx_accounts = []
|
||||
self.daily_lottery_limit = DAILY_LOTTERY_LIMIT
|
||||
self.delay_between_accounts = DELAY_BETWEEN_ACCOUNTS
|
||||
self.lottery_interval_min = LOTTERY_INTERVAL_MIN
|
||||
self.lottery_interval_max = LOTTERY_INTERVAL_MAX
|
||||
self.pending_code = None
|
||||
|
||||
# 代理
|
||||
env_proxy = os.getenv('pgdl', '')
|
||||
if PROXY_DEFAULT:
|
||||
self.proxy_url = PROXY_DEFAULT
|
||||
elif env_proxy:
|
||||
self.proxy_url = env_proxy
|
||||
else:
|
||||
self.proxy_url = ""
|
||||
self.use_proxy = USE_PROXY and bool(self.proxy_url)
|
||||
self.current_proxy = None
|
||||
self.codes_exhausted = False
|
||||
self.scan_loc_index = 0
|
||||
self.ua_index = 0
|
||||
self.ssl_error_count = 0
|
||||
|
||||
# 码字从文件加载
|
||||
self.code_list = self.read_codes_from_file()
|
||||
|
||||
self.load_config()
|
||||
self.setup_clients()
|
||||
|
||||
logger.info("🏮 王老吉扫码抽奖启动")
|
||||
logger.info(f"👥 账号: {len(self.wx_accounts)} | 🎯 每号抽奖: {self.daily_lottery_limit} 次 | 🔢 码字剩余: {len(self.code_list)}")
|
||||
if self.use_proxy:
|
||||
logger.info(f"🌐 代理已启用")
|
||||
|
||||
# ==================== 码字文件管理 ====================
|
||||
def read_codes_from_file(self) -> List[str]:
|
||||
"""从 WLJ_MZ.txt 读取所有码字"""
|
||||
try:
|
||||
if os.path.exists(CODE_FILE):
|
||||
with open(CODE_FILE, 'r', encoding='utf-8') as f:
|
||||
codes = [line.strip() for line in f if line.strip()]
|
||||
return codes
|
||||
else:
|
||||
logger.error(f"❌ 码字文件不存在: {CODE_FILE}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 读取码字文件失败: {e}")
|
||||
return []
|
||||
|
||||
def remove_code_from_file(self, code: str):
|
||||
"""从 WLJ_MZ.txt 中删除指定码字"""
|
||||
try:
|
||||
codes = self.read_codes_from_file()
|
||||
if code in codes:
|
||||
codes.remove(code)
|
||||
with open(CODE_FILE, 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(codes))
|
||||
logger.info(f"✅ 已从文件删除码字,剩余 {len(codes)} 个")
|
||||
else:
|
||||
logger.warning(f"⚠️ 码字不在文件中: {code}")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 删除码字失败: {e}")
|
||||
|
||||
def get_scan_code(self) -> Optional[str]:
|
||||
"""获取一个码字(优先待重试的,其次从列表第一个取)"""
|
||||
if self.pending_code:
|
||||
code = self.pending_code
|
||||
self.pending_code = None
|
||||
logger.info(f"🔄 复用失败码: {code}")
|
||||
return code
|
||||
|
||||
if not self.code_list:
|
||||
self.codes_exhausted = True
|
||||
return None
|
||||
|
||||
code = self.code_list[0]
|
||||
logger.info(f"📄 获取码字: {code} (剩余 {len(self.code_list)-1})")
|
||||
return code
|
||||
|
||||
def commit_scan_code(self, code: str):
|
||||
"""确认码字使用成功,从列表和文件中删除"""
|
||||
if code in self.code_list:
|
||||
self.code_list.remove(code)
|
||||
self.remove_code_from_file(code)
|
||||
|
||||
def set_pending_code(self, code: str):
|
||||
"""将失败码字设置为下次优先使用(不删除)"""
|
||||
self.pending_code = code
|
||||
logger.warning(f"⚠️ 码字失败,顺延下次: {code}")
|
||||
|
||||
# ==================== 代理、位置、UA ====================
|
||||
def get_proxy(self) -> Optional[str]:
|
||||
if not self.proxy_url:
|
||||
return None
|
||||
try:
|
||||
resp = requests.get(self.proxy_url, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
return resp.text.strip()
|
||||
except Exception as e:
|
||||
logger.error(f"🌐 获取代理失败: {e}")
|
||||
return None
|
||||
|
||||
def get_scan_location(self):
|
||||
lat, lon = SCAN_LOCATIONS[self.scan_loc_index % len(SCAN_LOCATIONS)]
|
||||
self.scan_loc_index += 1
|
||||
return lat, lon
|
||||
|
||||
def get_ua(self):
|
||||
ua = USER_AGENTS[self.ua_index % len(USER_AGENTS)]
|
||||
self.ua_index += 1
|
||||
return ua
|
||||
|
||||
def get_proxies(self, proxy):
|
||||
if proxy:
|
||||
return {"http": f"http://{proxy}", "https": f"http://{proxy}"}
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def is_ssl_error(error_str: str) -> bool:
|
||||
return any(k in error_str.lower() for k in ['ssl', 'eof', 'ssl.c:', 'ssl_eof'])
|
||||
|
||||
def refresh_proxy_ip(self):
|
||||
self.ssl_error_count = 0
|
||||
new = self.get_proxy()
|
||||
if new:
|
||||
logger.info(f"🔄 SSL错误,更换IP: {new}")
|
||||
return new
|
||||
return None
|
||||
|
||||
# ==================== 账号加载 ====================
|
||||
def load_config(self):
|
||||
self.wx_accounts = self.parse_manual_accounts()
|
||||
|
||||
def setup_clients(self):
|
||||
self.session.headers.update({
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"charset": "utf-8",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"Referer": "https://servicewechat.com/wxd25dc8ba975776e3/409/page-frame.html",
|
||||
})
|
||||
|
||||
def parse_manual_accounts(self):
|
||||
accounts = []
|
||||
text = os.getenv("WLJ_ID", "").strip()
|
||||
if not text:
|
||||
logger.error("❌ 环境变量 WLJ_ID 为空")
|
||||
return accounts
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
parts = line.split("#")
|
||||
if len(parts) >= 3:
|
||||
accounts.append({
|
||||
"nickname": parts[0],
|
||||
"wxId": parts[1],
|
||||
"phone": parts[2],
|
||||
"wxName": parts[0],
|
||||
})
|
||||
accounts = [a for a in accounts if a.get("wxId") not in REMOVE_WXIDS]
|
||||
logger.info(f"✅ 加载账号 {len(accounts)} 个")
|
||||
return accounts
|
||||
|
||||
# ==================== 签名 ====================
|
||||
def compute_sign(self, user_code: str) -> str:
|
||||
secret = "cU9(yZ3{zD6!pE4.xX7#"
|
||||
return hashlib.md5((user_code + secret).encode()).hexdigest().upper()
|
||||
|
||||
# ==================== API调用 ====================
|
||||
def get_code_from_chicken_farm(self, wxid):
|
||||
try:
|
||||
url = f"{WX_CLOUD}/prod-api/wechat/api/getMiniProgramCode"
|
||||
headers = {"Authorization": AUTH_TOKEN, "Content-Type": "application/json"}
|
||||
payload = {"wxid": wxid, "appid": WX_APPID}
|
||||
resp = requests.post(url, json=payload, headers=headers, timeout=10)
|
||||
data = resp.json()
|
||||
if data.get("code") == 200 and data.get("data", {}).get("code"):
|
||||
return data["data"]["code"]
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 获取code失败: {e}")
|
||||
return None
|
||||
|
||||
def step1_wlj_login(self, code, proxy):
|
||||
url = f"{WLJ_BASE_URL}/userInfoMini/userMemberLogin"
|
||||
sign = self.compute_sign("")
|
||||
payload = {
|
||||
"code": code, "possessor": POSSESSOR,
|
||||
"userCode": "", "mallCode": MALL_CODE,
|
||||
"saleChannel": SALE_CHANNEL, "sign": sign
|
||||
}
|
||||
headers = {"User-Agent": self.get_ua(), "Content-Type": "application/x-www-form-urlencoded"}
|
||||
try:
|
||||
resp = self.session.post(url, data=urlencode(payload).encode(),
|
||||
headers=headers, proxies=self.get_proxies(proxy), timeout=10)
|
||||
result = resp.json()
|
||||
if result.get("success") and result.get("status") == 1:
|
||||
content = result["content"]
|
||||
if isinstance(content, dict):
|
||||
token = content.get("token")
|
||||
user = content.get("user_summary", {})
|
||||
return {
|
||||
"token": token,
|
||||
"openid": user.get("openid"),
|
||||
"userCode": user.get("userCode"),
|
||||
"phone": user.get("phone") or user.get("userPhone"),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 登录异常: {e}")
|
||||
return None
|
||||
|
||||
def step2_get_s3_token(self, wlj_token, serial_code, user_code, lat, lon, proxy):
|
||||
url = f"{WLJ_BASE_URL}/openapi/getToken"
|
||||
payload = {
|
||||
"serialCode": serial_code, "latitude": lat, "longitude": lon,
|
||||
"possessor": POSSESSOR, "userCode": user_code,
|
||||
"mallCode": MALL_CODE, "saleChannel": SALE_CHANNEL,
|
||||
}
|
||||
headers = {"Authorization": wlj_token, "User-Agent": self.get_ua()}
|
||||
try:
|
||||
resp = self.session.post(url, data=urlencode(payload).encode(),
|
||||
headers=headers, proxies=self.get_proxies(proxy), timeout=10)
|
||||
data = resp.json()
|
||||
if data.get("success") and data.get("status") == 1:
|
||||
return data["content"]
|
||||
except Exception as e:
|
||||
logger.error(f"❌ getToken失败: {e}")
|
||||
return None
|
||||
|
||||
def step3_s3_third_login(self, s3_token, proxy):
|
||||
url = f"{S3_BASE_URL}/openapi/promotion/consumer/auth/thirdLogin"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {s3_token}",
|
||||
"User-Agent": self.get_ua(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload = {"appid": WX_APPID, "sign": s3_token}
|
||||
try:
|
||||
resp = requests.post(url, json=payload, headers=headers,
|
||||
proxies=self.get_proxies(proxy), timeout=10)
|
||||
data = resp.json()
|
||||
if data.get("success") and data.get("code") == "200":
|
||||
return data["data"]["token"], False
|
||||
except Exception as e:
|
||||
is_ssl = self.is_ssl_error(str(e))
|
||||
logger.error(f"❌ thirdLogin异常: {e}")
|
||||
return None, is_ssl
|
||||
return None, False
|
||||
|
||||
def step4_scan_mask_code(self, s3_token, mask_code, lat, lon, proxy):
|
||||
url = f"{S3_BASE_URL}/openapi/promotion/campaignExecute/scanMaskCode"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {s3_token}",
|
||||
"User-Agent": self.get_ua(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload = {"maskCode": mask_code, "lng": lon, "lat": lat, "lastScanResult": False}
|
||||
try:
|
||||
resp = requests.post(url, json=payload, headers=headers,
|
||||
proxies=self.get_proxies(proxy), timeout=10)
|
||||
data = resp.json()
|
||||
if data.get("success") and data.get("code") == "200":
|
||||
biz_msg = data["data"].get("bizMessage", "")
|
||||
if "已超上限" in biz_msg or "超限" in biz_msg:
|
||||
return data["data"], False, True
|
||||
if data["data"].get("bizCode") == "0000000":
|
||||
return data["data"], False, False
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 扫码失败: {e}")
|
||||
is_proxy_err = any(k in str(e) for k in ['Proxy', 'timeout', 'Cannot connect'])
|
||||
return None, is_proxy_err, False
|
||||
return None, False, False
|
||||
|
||||
def step5_mask_code_lottery(self, s3_token, mask_code, lat, lon, proxy):
|
||||
url = f"{S3_BASE_URL}/openapi/promotion/campaignExecute/maskCodeLottery"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {s3_token}",
|
||||
"User-Agent": self.get_ua(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload = {"maskCode": mask_code, "lng": lon, "lat": lat}
|
||||
try:
|
||||
resp = requests.post(url, json=payload, headers=headers,
|
||||
proxies=self.get_proxies(proxy), timeout=10)
|
||||
data = resp.json()
|
||||
if data.get("success") and data.get("code") == "200":
|
||||
return data["data"], False
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 抽奖失败: {e}")
|
||||
is_proxy_err = any(k in str(e) for k in ['Proxy', 'timeout', 'Cannot connect'])
|
||||
return None, is_proxy_err
|
||||
return None, False
|
||||
|
||||
def step6_receive_reward(self, s3_token, user_reward_id, proxy):
|
||||
url = f"{S3_BASE_URL}/openapi/promotion/consumer/receiveReward"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {s3_token}",
|
||||
"User-Agent": self.get_ua(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload = {"userRewardId": user_reward_id}
|
||||
try:
|
||||
resp = requests.post(url, json=payload, headers=headers,
|
||||
proxies=self.get_proxies(proxy), timeout=10)
|
||||
data = resp.json()
|
||||
if data.get("success") and data.get("code") == "200":
|
||||
if data["data"].get("bizCode") == "0000000":
|
||||
return data["data"]["data"], False
|
||||
except Exception as e:
|
||||
is_proxy = any(k in str(e) for k in ['Proxy', 'timeout', 'Cannot connect'])
|
||||
return None, is_proxy
|
||||
return None, False
|
||||
|
||||
def step7_claim_reward(self, s3_token, user_reward_id, proxy):
|
||||
url = f"{S3_BASE_URL}/openapi/promotion/consumer/claimReward"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {s3_token}",
|
||||
"User-Agent": self.get_ua(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload = {"id": user_reward_id}
|
||||
try:
|
||||
resp = requests.post(url, json=payload, headers=headers,
|
||||
proxies=self.get_proxies(proxy), timeout=10)
|
||||
data = resp.json()
|
||||
if data.get("success") and data.get("code") == "200":
|
||||
if data["data"].get("bizCode") == "0000000":
|
||||
return data["data"], False
|
||||
except Exception as e:
|
||||
is_proxy = any(k in str(e) for k in ['Proxy', 'timeout', 'Cannot connect'])
|
||||
return None, is_proxy
|
||||
return None, False
|
||||
|
||||
# ==================== 账号处理 ====================
|
||||
def process_account(self, account):
|
||||
result = {
|
||||
"nickname": account.get("wxName"),
|
||||
"wxid": account.get("wxId"),
|
||||
"phone": account.get("phone"),
|
||||
"attempts": 0, # 实际用码抽奖次数
|
||||
"hits": 0, # 中奖次数
|
||||
"prizes": [], # 去重奖品名称
|
||||
"total_amount": 0.0,
|
||||
"success": False,
|
||||
}
|
||||
|
||||
wxid = account.get("wxId")
|
||||
account_proxy = None
|
||||
if self.use_proxy:
|
||||
account_proxy = self.get_proxy()
|
||||
if account_proxy:
|
||||
logger.info(f"🌐 代理: {account_proxy}")
|
||||
|
||||
login_code = self.get_code_from_chicken_farm(wxid)
|
||||
if not login_code:
|
||||
logger.error("❌ 无code,跳过")
|
||||
return result
|
||||
|
||||
login_result = self.step1_wlj_login(login_code, account_proxy)
|
||||
if not login_result:
|
||||
logger.error("❌ 登录失败,跳过")
|
||||
return result
|
||||
|
||||
wlj_token = login_result["token"]
|
||||
phone = login_result.get("phone", "")
|
||||
logger.info(f"✅ 登录成功,手机: {phone}")
|
||||
|
||||
hits = 0
|
||||
attempts = 0
|
||||
current_proxy = account_proxy
|
||||
scan_lat, scan_lon = self.get_scan_location()
|
||||
logger.info(f"📍 扫码位置: {scan_lat},{scan_lon}")
|
||||
|
||||
seen_reward_ids = set() # 用于奖品去重
|
||||
|
||||
while hits < self.daily_lottery_limit and attempts < MAX_CODE_ATTEMPTS:
|
||||
logger.info(f"🎯 第 {hits+1}/{self.daily_lottery_limit} 次中奖 (已用码 {attempts})")
|
||||
|
||||
mask_code = self.get_scan_code()
|
||||
if not mask_code:
|
||||
break
|
||||
|
||||
# 步骤2
|
||||
s3_token = self.step2_get_s3_token(wlj_token, mask_code, login_result["userCode"],
|
||||
scan_lat, scan_lon, current_proxy)
|
||||
if not s3_token:
|
||||
self.set_pending_code(mask_code)
|
||||
time.sleep(random.uniform(self.lottery_interval_min, self.lottery_interval_max))
|
||||
continue
|
||||
|
||||
# 步骤3
|
||||
final_s3_token, is_ssl = self.step3_s3_third_login(s3_token, current_proxy)
|
||||
if is_ssl:
|
||||
self.ssl_error_count += 1
|
||||
self.set_pending_code(mask_code)
|
||||
if self.ssl_error_count >= 2:
|
||||
new_proxy = self.refresh_proxy_ip()
|
||||
if new_proxy:
|
||||
current_proxy = new_proxy
|
||||
else:
|
||||
break
|
||||
time.sleep(random.uniform(self.lottery_interval_min, self.lottery_interval_max))
|
||||
continue
|
||||
|
||||
if not final_s3_token:
|
||||
self.ssl_error_count = 0
|
||||
self.set_pending_code(mask_code)
|
||||
time.sleep(random.uniform(self.lottery_interval_min, self.lottery_interval_max))
|
||||
continue
|
||||
|
||||
# 步骤4
|
||||
scan_result, is_proxy_err, is_daily_limit = self.step4_scan_mask_code(
|
||||
final_s3_token, mask_code, scan_lat, scan_lon, current_proxy)
|
||||
if is_daily_limit:
|
||||
logger.warning("⚠️ 每日上限,停止本账号")
|
||||
break
|
||||
if not scan_result:
|
||||
if self.use_proxy and is_proxy_err:
|
||||
new_proxy = self.get_proxy()
|
||||
if new_proxy:
|
||||
current_proxy = new_proxy
|
||||
self.set_pending_code(mask_code)
|
||||
time.sleep(random.uniform(self.lottery_interval_min, self.lottery_interval_max))
|
||||
continue
|
||||
|
||||
# 扫码成功,使用码字
|
||||
attempts += 1
|
||||
|
||||
# 步骤5
|
||||
lottery_result, is_lottery_proxy_err = self.step5_mask_code_lottery(
|
||||
final_s3_token, mask_code, scan_lat, scan_lon, current_proxy)
|
||||
if not lottery_result:
|
||||
if self.use_proxy and is_lottery_proxy_err:
|
||||
new_proxy = self.get_proxy()
|
||||
if new_proxy:
|
||||
current_proxy = new_proxy
|
||||
lottery_result, _ = self.step5_mask_code_lottery(
|
||||
final_s3_token, mask_code, scan_lat, scan_lon, current_proxy)
|
||||
|
||||
if lottery_result:
|
||||
lucky_list = lottery_result.get("luckyRewardList", [])
|
||||
if lucky_list:
|
||||
hits += 1
|
||||
for prize in lucky_list:
|
||||
reward_id = prize.get("userRewardId")
|
||||
# 去重:同一个红包只记录一次
|
||||
if reward_id and reward_id in seen_reward_ids:
|
||||
continue
|
||||
if reward_id:
|
||||
seen_reward_ids.add(reward_id)
|
||||
|
||||
name = prize.get("rewardName", "未知")
|
||||
value = prize.get("rewardValue", 0)
|
||||
reward_type = prize.get("rewardType", "")
|
||||
result["prizes"].append(name)
|
||||
logger.info(f"🎁 获得 {name},价值 {value/100:.2f}元")
|
||||
|
||||
custom = prize.get("customData", {})
|
||||
if reward_id and reward_type == "redpacket" and custom.get("incentive"):
|
||||
logger.info("💰 红包已激活,领取中...")
|
||||
claim_data, is_claim_err = self.step6_receive_reward(final_s3_token, reward_id, current_proxy)
|
||||
if not claim_data and self.use_proxy and is_claim_err:
|
||||
new_proxy = self.get_proxy()
|
||||
if new_proxy:
|
||||
current_proxy = new_proxy
|
||||
claim_data, _ = self.step6_receive_reward(final_s3_token, reward_id, current_proxy)
|
||||
if claim_data:
|
||||
_, is_receive_err = self.step7_claim_reward(final_s3_token, reward_id, current_proxy)
|
||||
if _:
|
||||
result["total_amount"] += value / 100.0
|
||||
logger.info(f"💰 到账 {value/100:.2f}元")
|
||||
else:
|
||||
logger.info(f"🎟️ 未中奖")
|
||||
|
||||
# 提交码字使用
|
||||
self.commit_scan_code(mask_code)
|
||||
|
||||
if hits < self.daily_lottery_limit:
|
||||
time.sleep(random.uniform(self.lottery_interval_min, self.lottery_interval_max))
|
||||
|
||||
result["attempts"] = attempts
|
||||
result["hits"] = hits
|
||||
result["success"] = hits > 0
|
||||
return result
|
||||
|
||||
def run(self):
|
||||
if not self.wx_accounts:
|
||||
logger.error("❌ 无账号,退出")
|
||||
return
|
||||
|
||||
all_results = []
|
||||
total_attempts = 0
|
||||
total_amount = 0.0
|
||||
total_hits = 0
|
||||
|
||||
for idx, account in enumerate(self.wx_accounts):
|
||||
nick = account.get('wxName', '未知')
|
||||
logger.info(f"\n==== 账号 {idx+1}/{len(self.wx_accounts)}: {nick} ====")
|
||||
res = self.process_account(account)
|
||||
all_results.append(res)
|
||||
|
||||
total_attempts += res["attempts"]
|
||||
total_hits += res["hits"]
|
||||
total_amount += res["total_amount"]
|
||||
|
||||
cost = res["attempts"] * LOTTERY_CODE_PRICE
|
||||
profit = res["total_amount"] - cost
|
||||
logger.info(f"💰 账号{idx+1}: 抽奖{res['attempts']}次 | 中奖{res['hits']}次 | 金额{res['total_amount']:.2f} | 成本{cost:.2f} | 利润{profit:.2f}")
|
||||
|
||||
if self.codes_exhausted:
|
||||
logger.warning("⚠️ 码字耗尽,停止后续账号")
|
||||
break
|
||||
|
||||
if idx < len(self.wx_accounts) - 1:
|
||||
time.sleep(self.delay_between_accounts)
|
||||
|
||||
self.print_summary(all_results, total_attempts, total_hits, total_amount)
|
||||
|
||||
def print_summary(self, results, total_attempts, total_hits, total_amount):
|
||||
total_cost = total_attempts * LOTTERY_CODE_PRICE
|
||||
total_profit = total_amount - total_cost
|
||||
|
||||
logger.info("\n" + "="*40)
|
||||
logger.info("📊 执行汇总")
|
||||
logger.info(f"👥 账号: {len(results)} | ✅ 中奖账号: {sum(1 for r in results if r['success'])}")
|
||||
logger.info(f"🎰 总抽奖: {total_attempts} 次 | 🎁 中奖: {total_hits} 次")
|
||||
logger.info(f"💰 总金额: {total_amount:.2f} | 💵 总成本: {total_cost:.2f} | 📈 总利润: {total_profit:.2f}")
|
||||
logger.info("📋 详情:")
|
||||
for i, r in enumerate(results):
|
||||
status = "✅" if r["success"] else "❌"
|
||||
prizes = ", ".join(r["prizes"]) if r["prizes"] else "无"
|
||||
logger.info(f" {status} 账号{i+1}: {r['nickname']} | 抽奖{r['attempts']}次 | 中奖{r['hits']} | 金额{r['total_amount']:.2f} | {prizes}")
|
||||
|
||||
|
||||
def main():
|
||||
automation = WangLaoJiAutomation()
|
||||
automation.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user