Files
Yangmao_Script/WX_Applet/Applet_WLJi.py
2026-05-19 04:02:27 +08:00

717 lines
37 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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()