Files
qinglong/脚本库/微信小程序/协议版/快集合/2025-08-27_快集合_a9ad02b3.py
2026-05-24 05:33:05 +00:00

676 lines
25 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.
# Source: https://github.com/LinYuanovo/AutoTaskScripts/blob/main/miniapp/%E5%BF%AB%E9%9B%86%E5%90%88.py
# Raw: https://raw.githubusercontent.com/LinYuanovo/AutoTaskScripts/main/miniapp/%E5%BF%AB%E9%9B%86%E5%90%88.py
# Repo: LinYuanovo/AutoTaskScripts
# Path: miniapp/快集合.py
# UploadedAt: 2025-08-27T15:31:08+08:00
# SHA256: a9ad02b3ede5b222dc2998569d9a23fdf6e7700e14398f8d6c70a10cbe79b451
# Category: 微信小程序/协议版
# Evidence: getMiniProgramCode/wx_cloud/wx_token/code协议链路
"""
作者: 临渊
日期: 2025/7/21
name: 快集合
入口: 微信小程序 (https://wxaurl.cn/UOdOAJrVwan)
功能: 签到、看广告、查询积分
变量: kjh_openid = 'openid' (app/Exp/wxappLogin2 请求中的open_id)
定时: 一天两次
cron: 10 11,12 * * *
------------更新日志------------
2025/7/21 V1.0 初始化脚本
2025/7/28 V1.1 修改头部注释,以便拉库
"""
import json
import random
import time
import requests
import os
import logging
import traceback
import ssl
from datetime import datetime
MULTI_ACCOUNT_SPLIT = ["\n", "@"] # 多账号分隔符列表
MULTI_ACCOUNT_PROXY = False # 是否使用多账号代理默认不使用True则使用多账号代理
NOTIFY = os.getenv("LY_NOTIFY") or False # 是否推送日志默认不推送True则推送
class WechatCodeAdapter:
def __init__(self):
"""
初始化微信授权适配器
"""
self.wx_code_url = os.getenv("soy_codeurl_data")
self.wx_code_token = os.getenv("soy_codetoken_data")
self.wx_appid = "wx02092e8c44221583" # 微信小程序id
self.log_msgs = [] # 日志收集
self.setup_logging()
def setup_logging(self):
"""
配置日志系统
"""
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s\t- %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
handlers=[
# logging.FileHandler(f'{self.script_name}_{datetime.now().strftime("%Y%m%d")}.log', encoding='utf-8'), # 保存日志
logging.StreamHandler()
]
)
def log(self, msg, level="info"):
if level == "info":
logging.info(msg)
elif level == "error":
logging.error(msg)
elif level == "warning":
logging.warning(msg)
self.log_msgs.append(msg)
def get_protocol_type(self):
"""
获取协议类型
:return: 协议类型
"""
end_url = self.wx_code_url.split("/")[-1]
if end_url == "getMiniProgramCode":
# 养鸡场
return 1
elif end_url == "code":
# 牛子
return 2
elif end_url == "GetAllDevices":
# WeChatPadPro
return 3
elif end_url == "GetAuthKey":
# iwechat
return 4
else:
# 其他不知道的协议
return 0
def dict_keys_to_lower(self, obj):
"""
递归将字典的所有键名转为小写
"""
if isinstance(obj, dict):
return {k.lower(): self.dict_keys_to_lower(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [self.dict_keys_to_lower(i) for i in obj]
else:
return obj
def get_code_1(self, wx_id):
"""
养鸡场 获取code
:param wx_id: 微信id
:return: code
"""
try:
url = self.wx_code_url
headers = {
"Authorization": self.wx_code_token,
"Content-Type": "application/json"
}
payload = {"wxid": wx_id, "appid": self.wx_appid}
response = requests.post(url, headers=headers, json=payload, timeout=5)
response.raise_for_status()
# 将所有键名转为小写
response_json = self.dict_keys_to_lower(response.json())
# 直接取授权code不判断返回码code
code_value = response_json.get('data', {}).get('code', '')
if code_value:
code = code_value
return code
else:
self.log(f"[微信授权] 失败,错误信息: {response_json['message']}", level="error")
return False
except requests.RequestException as e:
self.log(f"[微信授权]发生网络错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
except Exception as e:
self.log(f"[微信授权]发生未知错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def get_code_2(self, wx_id):
"""
牛子 获取code
:param wx_id: 微信id
:return: code
"""
try:
url = self.wx_code_url
headers = {
"Content-Type": "application/json"
}
payload = {"wxid": wx_id, "appid": self.wx_appid}
response = requests.post(url, headers=headers, json=payload, timeout=5)
response.raise_for_status()
# 将所有键名转为小写
response_json = self.dict_keys_to_lower(response.json())
# 直接取授权code不判断返回码code
code_value = response_json.get('data', {}).get('code', '')
if code_value:
code = code_value
return code
else:
self.log(f"[微信授权] 失败,错误信息: {response_json['message']}", level="error")
return False
except requests.RequestException as e:
self.log(f"[微信授权]发生网络错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
except Exception as e:
self.log(f"[微信授权]发生未知错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def get_all_devices(self):
"""
WeChatPadPro 获取账号授权码列表
:return: 账号授权码列表
"""
try:
url = self.wx_code_url
params = {
"key": self.wx_code_token
}
response = requests.get(url, params=params, timeout=5)
response.raise_for_status()
response_json = response.json()
if response_json.get('Code') == 200:
all_devices = response_json.get('Data', {}).get('devices', [])
if all_devices:
return all_devices
else:
self.log(f"[获取账号授权码列表] 返回信息: {response_json['Text']}", level="error")
return []
else:
self.log(f"[获取账号授权码列表] 失败,错误信息: {response_json['Text']}", level="error")
return []
except Exception as e:
self.log(f"[获取账号授权码列表] 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return []
def get_target_key_by_wxid(self, all_keys, wx_id):
"""
获取指定微信id的授权码
:param all_keys: 所有账号授权码列表
:param wx_id: 微信id
:return: 指定微信id的授权码
"""
for key in all_keys:
_wx_id = key.get('deviceId') or key.get('wx_id')
if _wx_id == wx_id:
return key.get('authKey') or key.get('license')
return None
def get_code_3(self, wx_id):
"""
WeChatPadPro 获取code
:param wx_id: 微信id
:return: code
"""
try:
all_devices = self.get_all_devices()
target_key = self.get_target_key_by_wxid(all_devices, wx_id)
url = self.wx_code_url.split("/admin")[0] + "/applet/JsLogin"
params = {
"key": target_key
}
payload = {
"AppId": self.wx_appid,
"Data": "",
"Opt": 1,
"PackageName": "",
"SdkName": ""
}
response = requests.post(url, params=params, json=payload, timeout=5)
response.raise_for_status()
response_json = response.json()
if response_json.get('Code') == 200:
# self.log(f"[获取code] 成功code: {response_json.get('Data', {}).get('Code', '')}")
return response_json.get('Data', {}).get('Code', '')
else:
self.log(f"[获取code] 失败,错误信息: {response_json['Text']}", level="error")
return False
except Exception as e:
self.log(f"[获取code] 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def get_auth_keys(self, wx_id):
"""
iwechat 获取账号授权码列表
:return: 账号授权码列表
"""
try:
url = self.wx_code_url
params = {
"key": self.wx_code_token
}
response = requests.get(url, params=params, timeout=5)
response.raise_for_status()
response_json = response.json()
return response_json
except Exception as e:
self.log(f"[获取code] 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def get_code_4(self, wx_id):
"""
iwechat 获取code
:param wx_id: 微信id
:return: code
"""
try:
auth_keys = self.get_auth_keys()
target_key = self.get_target_key_by_wxid(auth_keys, wx_id)
url = self.wx_code_url.split("/admin")[0] + "/applet/JsLogin"
params = {
"key": target_key
}
payload = {
"AppId": self.wx_appid,
"Data": "",
"Opt": 1,
"PackageName": "",
"SdkName": ""
}
response = requests.post(url, params=params, json=payload, timeout=5)
response.raise_for_status()
response_json = response.json()
if response_json.get('Code') == 200:
self.log(f"[获取code] 成功code: {response_json.get('Data', {}).get('Code', '')}")
return response_json.get('Data', {}).get('Code', '')
else:
self.log(f"[获取code] 失败,错误信息: {response_json['Text']}", level="error")
return False
except Exception as e:
self.log(f"[获取code] 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def get_code(self, wx_id):
"""
获取code
:param wx_id: 微信id
:return: 指定wxid的code
"""
protocol_type = self.get_protocol_type()
if protocol_type == 1:
return self.get_code_1(wx_id)
elif protocol_type == 2:
return self.get_code_2(wx_id)
elif protocol_type == 3:
return self.get_code_3(wx_id)
elif protocol_type == 4:
return self.get_code_4(wx_id)
else:
self.log(f"[获取code] 发生错误: 未知协议类型", level="error")
return False
class TLSAdapter(requests.adapters.HTTPAdapter):
"""
自定义TLS
解决unsafe legacy renegotiation disabled
貌似python太高版本依然会报错
"""
def init_poolmanager(self, *args, **kwargs):
ctx = ssl.create_default_context()
ctx.set_ciphers("DEFAULT@SECLEVEL=1")
ctx.options |= 0x4 # <-- the key part here, OP_LEGACY_SERVER_CONNECT
kwargs["ssl_context"] = ctx
return super(TLSAdapter, self).init_poolmanager(*args, **kwargs)
class AutoTask:
def __init__(self, script_name):
"""
初始化自动任务类
:param script_name: 脚本名称,用于日志显示
"""
self.script_name = script_name
self.proxy_url = os.getenv("PROXY_API_URL") # 代理api返回一条txt文本内容为代理ip:端口
self.wechat_code_adapter = WechatCodeAdapter()
self.host = "exp2.jintaocms.top"
self.nickname = ""
self.user_id = ""
self.token = ""
self.user_agent = "Mozilla/5.0 (Linux; Android 12; M2012K11AC Build/SKQ1.220303.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.136 Mobile Safari/537.36 XWEB/1340129 MMWEBSDK/20240301 MMWEBID/9871 MicroMessenger/8.0.48.2580(0x28003036) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android"
def log(self, msg, level="info"):
self.wechat_code_adapter.log(msg, level)
def dict_keys_to_lower(self, obj):
"""
递归将字典的所有键名转为小写
"""
if isinstance(obj, dict):
return {k.lower(): self.dict_keys_to_lower(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [self.dict_keys_to_lower(i) for i in obj]
else:
return obj
def hide_phone(self, phone):
"""
隐藏手机号中间4位
"""
return phone[:3] + "****" + phone[-4:]
def get_proxy(self):
"""
获取代理
:return: 代理
"""
if not self.proxy_url:
self.log("[获取代理] 没有找到环境变量PROXY_API_URL不使用代理", level="warning")
return None
url = self.proxy_url
response = requests.get(url)
proxy = response.text
self.log(f"[获取代理] {proxy}")
return proxy
def check_proxy(self, proxy, session):
"""
检查代理
:param proxy: 代理
:param session: session
:return: 是否可用
"""
try:
url = f"https://{self.host}/"
response = session.get(url, timeout=5)
if response.status_code == 200:
self.log(f"[检查代理] {proxy} 应该可用")
return True
else:
self.log(f"[检查代理] {response.text}")
return False
except Exception as e:
return False
def check_env(self):
"""
检查环境变量
:return: 环境变量字符串
"""
try:
# 从环境变量获取cookie
kjh_openid = os.getenv(f"kjh_openid")
if not kjh_openid:
self.log("[检查环境变量] 没有找到环境变量kjh_openid请检查环境变量", level="error")
return None
# 自动检测分隔符
split_char = None
for sep in MULTI_ACCOUNT_SPLIT:
if sep in kjh_openid:
split_char = sep
break
if not split_char:
# 如果都没有分隔符,默认当作单账号
kjh_openids = [kjh_openid]
else:
kjh_openids = kjh_openid.split(split_char)
for kjh_openid in kjh_openids:
if "=" in kjh_openid:
kjh_openid = kjh_openid.split("=")[1]
yield kjh_openid
else:
yield kjh_openid
except Exception as e:
self.log(f"[检查环境变量] 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
raise
def get_openid(self, session, code):
"""
获取openid
:param session: session
:param code: 微信code
:return: openid
"""
try:
url = f"https://{self.host}/app/Exp/wxappLoginCode"
payload = {
"code": code
}
response = session.post(url, data=payload, timeout=5)
response_json = response.json()
if int(response_json['code']) == 1:
openid = response_json.get('data', {}).get('openid', '')
return openid
else:
self.log(f"[获取openid] 失败,错误信息: {response_json['msg']}", level="error")
return False
except Exception as e:
self.log(f"[获取openid] 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def save_account_info(self, account_info):
"""
保存账号信息
:param account_info: 账号信息(新获取的列表)
"""
file_path = "kjh_account_info.json"
# 读取旧数据
if os.path.exists(file_path):
with open(file_path, "r", encoding="utf-8") as f:
old_list = json.load(f)
else:
old_list = []
# 构建 wx_id 到账号的映射,方便查找和更新
old_dict = {item['wx_id']: item for item in old_list}
# self.log(f"旧数据: {old_dict}")
for new_item in account_info:
old_dict[new_item['wx_id']] = new_item # 有则更新,无则新增
with open(file_path, "w", encoding="utf-8") as f:
json.dump(list(old_dict.values()), f, ensure_ascii=False, indent=2)
self.log(f"保存新数据: 成功")
def load_account_info(self):
"""
加载账号信息
:return: 账号信息
"""
if os.path.exists("kjh_account_info.json"):
with open("kjh_account_info.json", "r", encoding="utf-8") as f:
account_info = json.load(f)
return account_info
else:
return []
def wxlogin(self, session, openid):
"""
登录
:param session: session
:param openid: openid
:return: 登录结果
"""
try:
url = f"https://{self.host}/app/Exp/wxappLogin2"
payload = {
"siright": 1,
"open_id": openid
}
response = session.post(url, data=payload, timeout=5)
response_json = response.json()
if int(response_json['code']) == 1:
self.user_id = response_json['data']['user_id']
self.token = response_json['data']['token']
self.nickname = response_json['data']['mobile']
return True
else:
self.log(f"[登录] 失败,错误信息: {response_json['msg']}", level="error")
return False
except requests.RequestException as e:
self.log(f"[登录] 发生网络错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
except Exception as e:
self.log(f"[登录] 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def get_sign_in_num(self, session):
"""
获取签到次数
:param session: session
:return: 签到次数
"""
try:
url = f"https://{self.host}/app/Exp/getUserCheckin"
payload = {
"token": self.token,
"user_id": self.user_id
}
response = session.post(url, data=payload, timeout=5)
response_json = response.json()
if int(response_json['code']) == 1:
return response_json['data']
else:
self.log(f"[获取签到次数] 失败,错误信息: {response_json['msg']}", level="error")
return 0
except Exception as e:
self.log(f"[获取签到次数] 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return 0
def sign_in(self, session, sign_in_num):
"""
签到
:param session: session
:return: 签到结果
"""
try:
url = f"https://{self.host}/app/Exp/useCheckin"
payload = {
"num": sign_in_num + 1,
"token": self.token,
"user_id": self.user_id
}
response = session.post(url, data=payload, timeout=5)
response_json = response.json()
if int(response_json['code']) == 1:
self.log(f"[{self.nickname}] 签到: 成功")
return True
else:
self.log(f"[{self.nickname}] 签到: {response_json['msg']}")
return False
except Exception as e:
self.log(f"[{self.nickname}] 签到: 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def watch_ad(self, session):
"""
看广告
:param session: session
:return: 看广告结果
"""
try:
url = f"https://{self.host}/app/Exp/reward"
params = {
"token": self.token,
"user_id": self.user_id
}
response = session.get(url, params=params, timeout=5)
response_json = response.json()
if int(response_json['c']) == 0:
self.log(f"[{self.nickname}] 看广告: 获得{response_json['d']['money']}积分")
return True
else:
self.log(f"[{self.nickname}] 看广告: {response_json['m']}")
return False
except Exception as e:
self.log(f"[{self.nickname}] 看广告: 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def get_user_info(self, session):
"""
获取用户信息
:param session: session
:return: 用户信息
"""
try:
url = f"https://{self.host}/app/Exp/getUserInfo"
payload = {
"token": self.token,
"user_id": self.user_id
}
response = session.post(url, data=payload, timeout=5)
response_json = response.json()
if int(response_json['code']) == 1:
self.log(f"[{self.nickname}] 积分: {response_json['data']['integral']}")
return True
else:
self.log(f"[{self.nickname}] 获取用户信息: {response_json['msg']}")
return False
except Exception as e:
self.log(f"[{self.nickname}] 获取用户信息: 发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
return False
def run(self):
"""
运行任务
"""
try:
self.log(f"{self.script_name}】开始执行任务")
self.log(f"{len(list(self.check_env()))}个账号")
for index, openid in enumerate(self.check_env(), 1):
self.log("")
self.log(f"------ 账号{index} 开始执行任务 ------")
session = requests.Session()
headers = {
"User-Agent": self.user_agent,
"Content-Type": "application/x-www-form-urlencoded",
"authority": self.host
}
session.headers.update(headers)
if MULTI_ACCOUNT_PROXY:
proxy = self.get_proxy()
if proxy:
session.proxies.update({"http": f"http://{proxy}", "https": f"http://{proxy}"})
# # 检查代理,不可用重新获取
# while not self.check_proxy(proxy, session):
# proxy = self.get_proxy()
# session.proxies.update({"http": f"http://{proxy}", "https": f"http://{proxy}"})
if openid:
if self.wxlogin(session, openid):
# 获取签到次数
sign_in_num = self.get_sign_in_num(session)
# 签到
self.sign_in(session, sign_in_num)
time.sleep(random.randint(3, 5))
# 看广告
while True:
if self.watch_ad(session):
time.sleep(random.randint(3, 5))
else:
break
# 获取用户信息
self.get_user_info(session)
else:
self.log(f"未找到openid请检查环境变量kjh_openid")
self.log(f"------ 账号{index} 执行任务完成 ------")
except Exception as e:
self.log(f"{self.script_name}】执行过程中发生错误: {str(e)}\n{traceback.format_exc()}", level="error")
finally:
if NOTIFY:
# 如果notify模块不存在从远程下载至本地
if not os.path.exists("notify.py"):
url = "https://raw.githubusercontent.com/whyour/qinglong/refs/heads/develop/sample/notify.py"
response = requests.get(url)
with open("notify.py", "w", encoding="utf-8") as f:
f.write(response.text)
import notify
else:
import notify
# 任务结束后推送日志
title = f"{self.script_name} 运行日志"
header = "作者:临渊\n"
content = header + "\n" +"\n".join(self.wechat_code_adapter.log_msgs)
notify.send(title, content)
if __name__ == "__main__":
auto_task = AutoTask("快集合")
auto_task.run()