添加 WX_Applet/Applet_JDBao.py
This commit is contained in:
675
WX_Applet/Applet_JDBao.py
Normal file
675
WX_Applet/Applet_JDBao.py
Normal file
@@ -0,0 +1,675 @@
|
|||||||
|
# cron: 12 8,12,18 * * *
|
||||||
|
# new Env("加多宝瓶盖")
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
青龙面板微信协议自动化脚本 - 加多宝扫码抽奖
|
||||||
|
基于HAR文件分析,接入养鸡场自动获取code
|
||||||
|
支持每个账号轮换扫码位置 + 码字文件管理(失败顺延、成功删除)
|
||||||
|
修复:识别“已经被核销”等错误,自动删除无效码字,避免死循环
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import io
|
||||||
|
import requests
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
from typing import List, Dict, Optional
|
||||||
|
|
||||||
|
# ==================== 养鸡场配置 ====================
|
||||||
|
WX_CLOUD = os.getenv('wx_cloud', 'http://192.168.31.203:666')
|
||||||
|
AUTH_TOKEN = os.getenv('wx_token', '')
|
||||||
|
SCAN_CODE_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "JDB_MZ.txt")
|
||||||
|
|
||||||
|
# ==================== 代理配置 ====================
|
||||||
|
PROXY_DEFAULT = ""
|
||||||
|
USE_PROXY = False
|
||||||
|
|
||||||
|
# ==================== 账号配置 ====================
|
||||||
|
USE_API_ACCOUNTS = False
|
||||||
|
JDB_ID = os.getenv('JDB_ID', '')
|
||||||
|
REMOVE_WXIDS = [
|
||||||
|
"wxid_x4nz2s4th42",
|
||||||
|
"wxid_fog306o9q22",
|
||||||
|
"wxid_tso944q0t22",
|
||||||
|
]
|
||||||
|
DELAY_BETWEEN_ACCOUNTS = 3
|
||||||
|
WX_APPID = "wx8371875e443e177f"
|
||||||
|
|
||||||
|
# ==================== 抽奖配置 ====================
|
||||||
|
DAILY_LOTTERY_LIMIT = 1 # 每账号最大成功抽奖次数
|
||||||
|
MAX_CODE_ATTEMPTS = 2 # 每账号最大使用码次数(包括成功和失败的码)
|
||||||
|
LOTTERY_INTERVAL_MIN = 5 # 最小抽奖间隔时间(秒)
|
||||||
|
LOTTERY_INTERVAL_MAX = 15 # 最大抽奖间隔时间(秒)
|
||||||
|
MAX_RETRY_COUNT = 3 # 最大重试次数
|
||||||
|
LOTTERY_CODE_PRICE = 0.22 # 有奖码统一价格(元)
|
||||||
|
|
||||||
|
# ==================== API配置 ====================
|
||||||
|
BASE_URL = "https://api-mp.jdbchina.com"
|
||||||
|
CLIENT_CODE = "CLI2113448692"
|
||||||
|
PROJECT_CODE = "86000021"
|
||||||
|
ACT_CODE = "ACT2512151610531"
|
||||||
|
|
||||||
|
# ==================== 扫码位置列表(纬度, 经度, 省份, 城市, 区县, 详细地址) ====================
|
||||||
|
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", "湖南省", "娄底市", "娄星区", "湖南省娄底市娄星区乐坪大道"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# ==================== 调试配置 ====================
|
||||||
|
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",
|
||||||
|
]
|
||||||
|
|
||||||
|
# 自定义日志格式
|
||||||
|
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 JiaduobaoAutomation:
|
||||||
|
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.scan_loc_index = 0 # 位置轮换索引
|
||||||
|
self.pending_code = None # 待重试的码字
|
||||||
|
self.codes_exhausted = False # 文件是否无码字
|
||||||
|
|
||||||
|
# 代理配置
|
||||||
|
env_proxy = os.getenv('pgdl', '')
|
||||||
|
if PROXY_DEFAULT:
|
||||||
|
self.proxy_url = PROXY_DEFAULT
|
||||||
|
logger.info(f"代理来源: 脚本配置")
|
||||||
|
elif env_proxy:
|
||||||
|
self.proxy_url = env_proxy
|
||||||
|
logger.info(f"代理来源: 青龙变量 pgdl")
|
||||||
|
else:
|
||||||
|
self.proxy_url = ""
|
||||||
|
logger.info(f"未配置代理")
|
||||||
|
self.use_proxy = USE_PROXY
|
||||||
|
self.current_proxy = None
|
||||||
|
|
||||||
|
self.load_config()
|
||||||
|
self.setup_clients()
|
||||||
|
|
||||||
|
logger.info("加多宝扫码抽奖 - 自动化脚本启动")
|
||||||
|
logger.info(f"账号数量: {len(self.wx_accounts)}")
|
||||||
|
logger.info(f"每账号执行: {self.daily_lottery_limit} 次抽奖")
|
||||||
|
logger.info(f"码字文件: {SCAN_CODE_FILE}")
|
||||||
|
if self.use_proxy and self.proxy_url:
|
||||||
|
logger.info(f"代理模式: 开启")
|
||||||
|
|
||||||
|
# ==================== 码字文件操作 ====================
|
||||||
|
def read_codes_from_file(self) -> List[str]:
|
||||||
|
"""从文件读取所有原始码字(每行一条)"""
|
||||||
|
try:
|
||||||
|
if os.path.exists(SCAN_CODE_FILE):
|
||||||
|
with open(SCAN_CODE_FILE, 'r', encoding='utf-8') as f:
|
||||||
|
lines = [line.strip() for line in f if line.strip()]
|
||||||
|
return lines
|
||||||
|
else:
|
||||||
|
logger.error(f"码字文件不存在: {SCAN_CODE_FILE}")
|
||||||
|
return []
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"读取码字文件失败: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def remove_used_code(self, code: str) -> bool:
|
||||||
|
"""从文件中删除指定实际码字对应的原始行"""
|
||||||
|
try:
|
||||||
|
raw_lines = self.read_codes_from_file()
|
||||||
|
# 查找匹配的行(原始行可能是URL,需提取实际码字比较)
|
||||||
|
target_raw = None
|
||||||
|
for raw in raw_lines:
|
||||||
|
if self._extract_code(raw) == code:
|
||||||
|
target_raw = raw
|
||||||
|
break
|
||||||
|
if target_raw:
|
||||||
|
raw_lines.remove(target_raw)
|
||||||
|
with open(SCAN_CODE_FILE, 'w', encoding='utf-8') as f:
|
||||||
|
f.write('\n'.join(raw_lines))
|
||||||
|
logger.info(f"已从文件删除码字: {code} (剩余: {len(raw_lines)} 个)")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning(f"码字不在文件中: {code}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除码字失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_code(raw: str) -> str:
|
||||||
|
"""从原始行中提取实际码字(URL取最后一段,否则去空格)"""
|
||||||
|
raw = raw.strip()
|
||||||
|
if '/' in raw:
|
||||||
|
return raw.split('/')[-1]
|
||||||
|
return raw
|
||||||
|
|
||||||
|
def get_pending_code(self) -> Optional[str]:
|
||||||
|
if self.pending_code:
|
||||||
|
code = self.pending_code
|
||||||
|
self.pending_code = None
|
||||||
|
logger.info(f"复用上次失败的码字: {code}")
|
||||||
|
return code
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_pending_code(self, code: str):
|
||||||
|
if code:
|
||||||
|
logger.warning(f"码字失败,顺延下次使用: {code}")
|
||||||
|
self.pending_code = code
|
||||||
|
|
||||||
|
def commit_scan_code(self, code: str):
|
||||||
|
"""确认码字已成功核销,从文件删除"""
|
||||||
|
self.remove_used_code(code)
|
||||||
|
|
||||||
|
# ==================== 位置轮换 ====================
|
||||||
|
def get_scan_location(self) -> tuple:
|
||||||
|
lat, lon, province, city, area, address = SCAN_LOCATIONS[self.scan_loc_index % len(SCAN_LOCATIONS)]
|
||||||
|
self.scan_loc_index += 1
|
||||||
|
return lat, lon, province, city, area, address
|
||||||
|
|
||||||
|
# ==================== 代理相关 ====================
|
||||||
|
def get_proxy(self) -> Optional[str]:
|
||||||
|
if not self.proxy_url:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
response = requests.get(self.proxy_url, timeout=10)
|
||||||
|
if response.status_code == 200:
|
||||||
|
try:
|
||||||
|
data = response.json()
|
||||||
|
if isinstance(data, dict) and "data" in data:
|
||||||
|
proxy_list = data["data"]
|
||||||
|
if isinstance(proxy_list, list) and len(proxy_list) > 0:
|
||||||
|
ip = proxy_list[0].get("ip")
|
||||||
|
port = proxy_list[0].get("port")
|
||||||
|
if ip and port:
|
||||||
|
proxy = f"{ip}:{port}"
|
||||||
|
logger.info(f"获取代理成功: {proxy}")
|
||||||
|
return proxy
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
proxy = response.text.strip()
|
||||||
|
if proxy:
|
||||||
|
logger.info(f"获取代理成功: {proxy}")
|
||||||
|
return proxy
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取代理失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_proxies(self, proxy: Optional[str] = None) -> Optional[dict]:
|
||||||
|
if proxy:
|
||||||
|
return {"http": f"http://{proxy}", "https": f"http://{proxy}"}
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ==================== 账号加载 ====================
|
||||||
|
def load_config(self):
|
||||||
|
if USE_API_ACCOUNTS:
|
||||||
|
self.wx_accounts = self.get_wechat_account_list()
|
||||||
|
else:
|
||||||
|
self.wx_accounts = self.parse_manual_accounts()
|
||||||
|
|
||||||
|
def setup_clients(self):
|
||||||
|
self.session.headers.update({
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"charset": "utf-8",
|
||||||
|
"Accept-Encoding": "gzip, deflate, br",
|
||||||
|
"Referer": "https://servicewechat.com/wx8371875e443e177f/30/page-frame.html",
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_wechat_account_list(self) -> List[Dict]:
|
||||||
|
if not AUTH_TOKEN:
|
||||||
|
logger.error("未设置wx_token")
|
||||||
|
return []
|
||||||
|
url = f"{WX_CLOUD}/prod-api/wechat/wechat/list?pageNum=1&pageSize=1000"
|
||||||
|
headers = {"Authorization": AUTH_TOKEN, "Content-Type": "application/json"}
|
||||||
|
try:
|
||||||
|
resp = requests.get(url, headers=headers, timeout=10)
|
||||||
|
resp.raise_for_status()
|
||||||
|
data = resp.json()
|
||||||
|
if data.get("code") == 200 and isinstance(data.get("rows"), list):
|
||||||
|
accounts = [a for a in data["rows"] if a.get("wxId") not in REMOVE_WXIDS]
|
||||||
|
logger.info(f"养鸡场获取账号成功: {len(accounts)} 个")
|
||||||
|
return accounts
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取养鸡场账号失败: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def parse_manual_accounts(self) -> List[Dict]:
|
||||||
|
accounts = []
|
||||||
|
if not JDB_ID:
|
||||||
|
return accounts
|
||||||
|
for acc_str in JDB_ID.split():
|
||||||
|
parts = acc_str.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]
|
||||||
|
return accounts
|
||||||
|
|
||||||
|
# ==================== 养鸡场获取jscode ====================
|
||||||
|
def get_code_from_chicken_farm(self, wxid: str = None) -> Optional[str]:
|
||||||
|
try:
|
||||||
|
url = f"{WX_CLOUD}/prod-api/wechat/api/getMiniProgramCode"
|
||||||
|
headers = {"Authorization": AUTH_TOKEN, "Content-Type": "application/json"}
|
||||||
|
payload = {"wxid": wxid or "", "appid": WX_APPID}
|
||||||
|
resp = requests.post(url, json=payload, headers=headers, timeout=10)
|
||||||
|
resp.raise_for_status()
|
||||||
|
data = resp.json()
|
||||||
|
if data.get("code") == 200 and data.get("data", {}).get("code"):
|
||||||
|
code = data["data"]["code"]
|
||||||
|
logger.info(f"获取jscode成功: {code}")
|
||||||
|
return code
|
||||||
|
else:
|
||||||
|
logger.warning("养鸡场无可用code")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取code失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ==================== 扫码码获取(文件+待重试) ====================
|
||||||
|
def get_scan_code(self) -> Optional[str]:
|
||||||
|
if self.codes_exhausted:
|
||||||
|
logger.warning("扫码码已用完,不再获取")
|
||||||
|
return None
|
||||||
|
pending = self.get_pending_code()
|
||||||
|
if pending:
|
||||||
|
return pending
|
||||||
|
codes = self.read_codes_from_file()
|
||||||
|
if codes:
|
||||||
|
raw = codes[0]
|
||||||
|
code = self._extract_code(raw)
|
||||||
|
logger.info(f"从文件获取扫码码: {code} (剩余: {len(codes)-1} 个)")
|
||||||
|
return code
|
||||||
|
else:
|
||||||
|
logger.warning("文件中无码字")
|
||||||
|
self.codes_exhausted = True
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ==================== API 请求方法 ====================
|
||||||
|
@staticmethod
|
||||||
|
def extract_prize_amount(prize_name: str) -> float:
|
||||||
|
if not prize_name or "积分" in prize_name or "谢谢参与" in prize_name:
|
||||||
|
return 0.0
|
||||||
|
match = re.search(r'(\d+(?:\.\d+)?)\s*元', prize_name)
|
||||||
|
return float(match.group(1)) if match else 0.0
|
||||||
|
|
||||||
|
def login(self, jscode: str, proxy: Optional[str] = None) -> Optional[Dict]:
|
||||||
|
url = f"{BASE_URL}/geement.authjextra/api/v1/loginsession/2weichatmicroprogram"
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
"User-Agent": random.choice(USER_AGENTS),
|
||||||
|
"Referer": "https://servicewechat.com/wx8371875e443e177f/30/page-frame.html",
|
||||||
|
}
|
||||||
|
data = f"jscode={jscode}&app_id={WX_APPID}&client_code={CLIENT_CODE}"
|
||||||
|
try:
|
||||||
|
resp = self.session.post(url, data=data, headers=headers, proxies=self.get_proxies(proxy), timeout=10)
|
||||||
|
resp.raise_for_status()
|
||||||
|
result = resp.json()
|
||||||
|
if result.get("success"):
|
||||||
|
logger.info(f"登录成功, user_id: {result['data'].get('user_id')}")
|
||||||
|
return {"token": result["data"]["token"], "user_id": result["data"].get("user_id"), "open_id": result["data"].get("open_id")}
|
||||||
|
else:
|
||||||
|
logger.error(f"登录失败: {result.get('msg')}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"登录请求失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_user_info(self, token: str, proxy: Optional[str] = None):
|
||||||
|
url = f"{BASE_URL}/geement.usercenter/api/v1/user/information"
|
||||||
|
headers = {"apitoken": token, "User-Agent": random.choice(USER_AGENTS)}
|
||||||
|
try:
|
||||||
|
resp = self.session.get(url, headers=headers, proxies=self.get_proxies(proxy), timeout=10)
|
||||||
|
if resp.status_code == 200 and resp.json().get("success"):
|
||||||
|
info = resp.json()["data"]
|
||||||
|
logger.info(f"用户: {info.get('nick_name', '')[:16]}, 等级: {info.get('levelinfo', {}).get('level_name', '未知')}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取用户信息失败: {e}")
|
||||||
|
|
||||||
|
def scan_verify(self, token: str, code_number: str, proxy: Optional[str] = None) -> tuple:
|
||||||
|
url = f"{BASE_URL}/thirty.jdb/api/lottery/code/outcheck?project_code={PROJECT_CODE}&code={code_number}"
|
||||||
|
headers = {"apitoken": token, "User-Agent": random.choice(USER_AGENTS)}
|
||||||
|
try:
|
||||||
|
resp = self.session.get(url, headers=headers, proxies=self.get_proxies(proxy), timeout=10)
|
||||||
|
resp.raise_for_status()
|
||||||
|
result = resp.json()
|
||||||
|
if result.get("success"):
|
||||||
|
logger.info(f"扫码验证成功: 箱号={result['data'].get('boxNumber')}")
|
||||||
|
return result["data"], False
|
||||||
|
else:
|
||||||
|
logger.error(f"扫码验证失败: {result.get('msg')}")
|
||||||
|
return None, False
|
||||||
|
except Exception as e:
|
||||||
|
error_str = str(e)
|
||||||
|
is_proxy_err = any(k in error_str for k in ['Proxy', 'timeout', 'Cannot connect'])
|
||||||
|
logger.error(f"扫码验证异常: {e}")
|
||||||
|
return None, is_proxy_err
|
||||||
|
|
||||||
|
def luckdraw(self, token: str, code_number: str, lat: str, lon: str, province: str, city: str, area: str, address: str, proxy: Optional[str] = None) -> tuple:
|
||||||
|
url = f"{BASE_URL}/thirty.jdb/api/lottery"
|
||||||
|
headers = {
|
||||||
|
"apitoken": token,
|
||||||
|
"Content-Type": "application/json-patch+json",
|
||||||
|
"actcode": ACT_CODE,
|
||||||
|
"User-Agent": random.choice(USER_AGENTS),
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
"code": code_number,
|
||||||
|
"province_name": province,
|
||||||
|
"city_name": city,
|
||||||
|
"area_name": area,
|
||||||
|
"address": address,
|
||||||
|
"longitude": float(lon),
|
||||||
|
"dimension": float(lat),
|
||||||
|
"project_code": PROJECT_CODE
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
resp = self.session.post(url, json=payload, headers=headers, proxies=self.get_proxies(proxy), timeout=10)
|
||||||
|
resp.raise_for_status()
|
||||||
|
result = resp.json()
|
||||||
|
if result.get("success"):
|
||||||
|
prize = result["data"].get("lotterydata", {}).get("prizedto", {})
|
||||||
|
if prize:
|
||||||
|
name = prize.get("prize_name", "")
|
||||||
|
amount = self.extract_prize_amount(name)
|
||||||
|
logger.info(f"抽奖结果: {name}")
|
||||||
|
return {"prize_name": name, "prize_value": amount}, False, ""
|
||||||
|
else:
|
||||||
|
logger.info("抽奖结果: 谢谢参与")
|
||||||
|
return {"prize_name": "谢谢参与", "prize_value": 0}, False, ""
|
||||||
|
else:
|
||||||
|
msg = result.get("msg", "")
|
||||||
|
if "已经抽过" in msg or "已经扫码" in msg:
|
||||||
|
logger.warning(f"抽奖失败: {msg} (码已使用)")
|
||||||
|
elif "抽奖次数已经达到最大" in msg:
|
||||||
|
logger.warning(f"抽奖失败: {msg} (当前码保留)")
|
||||||
|
else:
|
||||||
|
logger.error(f"抽奖失败: {msg}")
|
||||||
|
return None, False, msg
|
||||||
|
except Exception as e:
|
||||||
|
error_str = str(e)
|
||||||
|
is_proxy_err = any(k in error_str for k in ['Proxy', 'timeout', 'Cannot connect'])
|
||||||
|
logger.error(f"抽奖请求失败: {e}")
|
||||||
|
return None, is_proxy_err, error_str
|
||||||
|
|
||||||
|
def get_win_goods(self, token: str, proxy: Optional[str] = None) -> Optional[List]:
|
||||||
|
url = f"{BASE_URL}/geement.actjextra/api/v1/act/win/goods?act_codes={ACT_CODE}"
|
||||||
|
headers = {"apitoken": token, "User-Agent": random.choice(USER_AGENTS)}
|
||||||
|
try:
|
||||||
|
resp = self.session.get(url, headers=headers, proxies=self.get_proxies(proxy), timeout=10)
|
||||||
|
if resp.status_code == 200 and resp.json().get("success"):
|
||||||
|
goods = resp.json().get("data", [])
|
||||||
|
logger.info(f"中奖记录: {len(goods)} 条")
|
||||||
|
return goods
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"查询中奖记录失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ==================== 单个账号处理 ====================
|
||||||
|
def process_account(self, account: Dict) -> Dict:
|
||||||
|
result = {
|
||||||
|
"nickname": account.get("wxName", ""),
|
||||||
|
"wxid": account.get("wxId", ""),
|
||||||
|
"phone": account.get("phone", ""),
|
||||||
|
"lottery_count": 0,
|
||||||
|
"code_used_count": 0,
|
||||||
|
"prizes": [],
|
||||||
|
"total_amount": 0.0,
|
||||||
|
"win_records": [],
|
||||||
|
"success": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
wxid = account.get("wxId", "")
|
||||||
|
# 每个账号轮换一个位置
|
||||||
|
lat, lon, province, city, area, address = self.get_scan_location()
|
||||||
|
logger.info(f"当前账号位置: {province}{city}{area} {address} ({lat},{lon})")
|
||||||
|
|
||||||
|
# 代理
|
||||||
|
account_proxy = None
|
||||||
|
if self.use_proxy:
|
||||||
|
account_proxy = self.get_proxy()
|
||||||
|
if account_proxy:
|
||||||
|
logger.info(f"使用代理: {account_proxy}")
|
||||||
|
|
||||||
|
# 登录
|
||||||
|
jscode = self.get_code_from_chicken_farm(wxid)
|
||||||
|
if not jscode:
|
||||||
|
logger.error("无法获取jscode,跳过")
|
||||||
|
return result
|
||||||
|
|
||||||
|
login_result = None
|
||||||
|
for retry in range(MAX_RETRY_COUNT):
|
||||||
|
login_result = self.login(jscode, account_proxy)
|
||||||
|
if login_result:
|
||||||
|
break
|
||||||
|
if self.use_proxy and account_proxy:
|
||||||
|
new_proxy = self.get_proxy()
|
||||||
|
if new_proxy:
|
||||||
|
account_proxy = new_proxy
|
||||||
|
logger.info(f"更换代理: {account_proxy}")
|
||||||
|
continue
|
||||||
|
# 如果还有重试机会,重新获取jscode
|
||||||
|
if retry < MAX_RETRY_COUNT - 1 and USE_API_ACCOUNTS:
|
||||||
|
new_code = self.get_code_from_chicken_farm(wxid)
|
||||||
|
if new_code:
|
||||||
|
jscode = new_code
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
|
||||||
|
if not login_result:
|
||||||
|
logger.error("登录失败,跳过")
|
||||||
|
return result
|
||||||
|
|
||||||
|
token = login_result["token"]
|
||||||
|
self.get_user_info(token, account_proxy)
|
||||||
|
|
||||||
|
# 抽奖循环
|
||||||
|
success_cnt = 0
|
||||||
|
used_cnt = 0
|
||||||
|
current_proxy = account_proxy
|
||||||
|
|
||||||
|
while success_cnt < self.daily_lottery_limit:
|
||||||
|
if used_cnt >= MAX_CODE_ATTEMPTS:
|
||||||
|
logger.warning(f"已达最大尝试次数 {MAX_CODE_ATTEMPTS},退出")
|
||||||
|
break
|
||||||
|
|
||||||
|
scan_code = self.get_scan_code()
|
||||||
|
if not scan_code:
|
||||||
|
break
|
||||||
|
|
||||||
|
logger.info(f"第 {success_cnt+1}/{self.daily_lottery_limit} 次抽奖 (已用码数: {used_cnt})")
|
||||||
|
|
||||||
|
# 扫码验证
|
||||||
|
scan_res, proxy_err = self.scan_verify(token, scan_code, current_proxy)
|
||||||
|
if not scan_res:
|
||||||
|
if self.use_proxy and proxy_err:
|
||||||
|
new_proxy = self.get_proxy()
|
||||||
|
if new_proxy:
|
||||||
|
current_proxy = new_proxy
|
||||||
|
logger.info(f"更换代理重试扫码")
|
||||||
|
scan_res, _ = self.scan_verify(token, scan_code, current_proxy)
|
||||||
|
if not scan_res:
|
||||||
|
used_cnt += 1
|
||||||
|
result["code_used_count"] = used_cnt
|
||||||
|
# 网络类失败,保留码字
|
||||||
|
self.set_pending_code(scan_code)
|
||||||
|
time.sleep(random.uniform(self.lottery_interval_min, self.lottery_interval_max))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 抽奖
|
||||||
|
lottery_res, lottery_proxy_err, err_msg = self.luckdraw(
|
||||||
|
token, scan_code, lat, lon, province, city, area, address, current_proxy
|
||||||
|
)
|
||||||
|
if not lottery_res and self.use_proxy and lottery_proxy_err:
|
||||||
|
new_proxy = self.get_proxy()
|
||||||
|
if new_proxy:
|
||||||
|
current_proxy = new_proxy
|
||||||
|
lottery_res, _, err_msg = self.luckdraw(
|
||||||
|
token, scan_code, lat, lon, province, city, area, address, current_proxy
|
||||||
|
)
|
||||||
|
|
||||||
|
if lottery_res is not None:
|
||||||
|
# 抽奖成功(包括谢谢参与),码字已被平台核销,删除文件中的该码
|
||||||
|
self.commit_scan_code(scan_code)
|
||||||
|
used_cnt += 1
|
||||||
|
result["code_used_count"] = used_cnt
|
||||||
|
success_cnt += 1
|
||||||
|
result["lottery_count"] = success_cnt
|
||||||
|
prize_val = lottery_res.get("prize_value", 0)
|
||||||
|
if prize_val > 0:
|
||||||
|
result["prizes"].append(lottery_res.get("prize_name", ""))
|
||||||
|
result["total_amount"] += prize_val
|
||||||
|
else:
|
||||||
|
# ========== 修复点:增加“已经被核销”等永久性失败的判断 ==========
|
||||||
|
# 定义需要删除码字的错误关键词(码已失效)
|
||||||
|
consumed_keywords = ["已经抽过", "已经扫码", "已经被核销", "已核销", "已使用", "串码已经被核销"]
|
||||||
|
if any(keyword in err_msg for keyword in consumed_keywords):
|
||||||
|
# 码已被消耗,删除它
|
||||||
|
self.commit_scan_code(scan_code)
|
||||||
|
used_cnt += 1
|
||||||
|
result["code_used_count"] = used_cnt
|
||||||
|
logger.info(f"码字已失效,已从文件删除: {scan_code}")
|
||||||
|
elif "抽奖次数已经达到最大" in err_msg:
|
||||||
|
# 账号达上限,码字未消耗,保留给下一个账号
|
||||||
|
self.set_pending_code(scan_code)
|
||||||
|
logger.warning("当前账号已达上限,切换账号")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# 其他失败(如网络、代理错误),保留码字重试
|
||||||
|
self.set_pending_code(scan_code)
|
||||||
|
used_cnt += 1
|
||||||
|
result["code_used_count"] = used_cnt
|
||||||
|
|
||||||
|
if success_cnt < self.daily_lottery_limit:
|
||||||
|
time.sleep(random.uniform(self.lottery_interval_min, self.lottery_interval_max))
|
||||||
|
|
||||||
|
result["success"] = result["lottery_count"] > 0
|
||||||
|
|
||||||
|
# 查询中奖记录
|
||||||
|
if result["lottery_count"] > 0:
|
||||||
|
win = self.get_win_goods(token, account_proxy)
|
||||||
|
if win:
|
||||||
|
result["win_records"] = win
|
||||||
|
for g in win:
|
||||||
|
status = "已发放" if g.get("grant_status") == 30 else "未发放"
|
||||||
|
logger.info(f"中奖: {g.get('win_prize_name')} | {status}")
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
# ==================== 主流程 ====================
|
||||||
|
def run(self):
|
||||||
|
if not self.wx_accounts:
|
||||||
|
logger.error("没有可用账号")
|
||||||
|
return
|
||||||
|
all_results = []
|
||||||
|
total_lottery = 0
|
||||||
|
total_amount = 0.0
|
||||||
|
success_count = 0
|
||||||
|
|
||||||
|
for idx, acc in enumerate(self.wx_accounts):
|
||||||
|
nickname = acc.get('wxName', '未知')
|
||||||
|
logger.info(f"\n========== 账号 {idx+1}/{len(self.wx_accounts)} {nickname} ==========")
|
||||||
|
res = self.process_account(acc)
|
||||||
|
all_results.append(res)
|
||||||
|
total_lottery += res["lottery_count"]
|
||||||
|
total_amount += res["total_amount"]
|
||||||
|
if res["success"]:
|
||||||
|
success_count += 1
|
||||||
|
|
||||||
|
cost = res.get("code_used_count", res["lottery_count"]) * LOTTERY_CODE_PRICE
|
||||||
|
profit = res["total_amount"] - cost
|
||||||
|
logger.info(f"账号 {idx+1} 成功抽奖: {res['lottery_count']}次, 用码: {res.get('code_used_count',0)}个, 金额: {res['total_amount']:.2f}元, 成本: {cost:.2f}元, 利润: {profit:.2f}元")
|
||||||
|
|
||||||
|
if self.codes_exhausted:
|
||||||
|
logger.warning("码字文件已无码,停止后续账号")
|
||||||
|
break
|
||||||
|
if idx < len(self.wx_accounts) - 1:
|
||||||
|
logger.info(f"等待 {self.delay_between_accounts} 秒...")
|
||||||
|
time.sleep(self.delay_between_accounts)
|
||||||
|
|
||||||
|
# 汇总
|
||||||
|
total_used = sum(r.get("code_used_count", 0) for r in all_results)
|
||||||
|
logger.info("\n" + "="*50)
|
||||||
|
logger.info("执行汇总")
|
||||||
|
logger.info(f"总账号数: {len(all_results)} 成功: {success_count}")
|
||||||
|
logger.info(f"总成功抽奖: {total_lottery} 次")
|
||||||
|
logger.info(f"总用码: {total_used} 个")
|
||||||
|
logger.info(f"总金额: {total_amount:.2f} 元")
|
||||||
|
logger.info(f"总成本: {total_used * LOTTERY_CODE_PRICE:.2f} 元")
|
||||||
|
logger.info(f"总利润: {total_amount - total_used * LOTTERY_CODE_PRICE:.2f} 元")
|
||||||
|
|
||||||
|
for i, r in enumerate(all_results):
|
||||||
|
status = "✓" if r["success"] else "✗"
|
||||||
|
logger.info(f" {status} 账号{i+1}: {r['nickname']} | 抽奖: {r['lottery_count']}次 | 金额: {r['total_amount']:.2f}元")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
JiaduobaoAutomation().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user