This commit is contained in:
2025-08-10 03:39:05 +08:00
commit 088b76c8bb
5 changed files with 1091 additions and 0 deletions

409
main.py Normal file
View File

@@ -0,0 +1,409 @@
import requests
import csv
from datetime import datetime
import subprocess
import time
import logging
# 配置常量
GIT_INTERVAL = 86400 # Git提交间隔(秒)
STATS_INTERVAL = 3600 # BTC Plus统计数据获取间隔(秒)
REWARDS_INTERVAL = 86400 # BTC Plus奖励数据获取间隔(秒)
ALLOCATIONS_INTERVAL = 3600 # BTC Plus分配数据获取间隔(秒)
POINTS_INTERVAL = 86400 # Phase2积分系统数据获取间隔(秒)
FETCH_INTERVAL = 600 # 数据获取间隔(秒)
LOG_FILE = "solv_btc_plus.log"
def timestamp_to_datetime(timestamp):
"""将时间戳转换为可读时间字符串"""
return (
datetime.fromtimestamp(timestamp).isoformat()
if timestamp
else datetime.now().isoformat()
)
def get_current_time():
"""获取当前时间字符串"""
return datetime.now().isoformat()
def fetch_btc_plus_reward(address, stage_no=1):
"""从API获取BTC Plus奖励数据"""
url = "https://graphql.sft-api.com/graphql"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0",
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Referer": "https://app.solv.finance/",
"content-type": "application/json",
"authorization": "dW5kZWZpbmVkfHx1bmRlZmluZWR8fHVuZGVmaW5lZHx8.undefined",
"x-amz-user-agent": "aws-amplify/3.0.7",
"Origin": "https://app.solv.finance",
"DNT": "1",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "cross-site",
"Sec-GPC": "1",
"Priority": "u=4",
}
query = """
query BtcPlusRewardByAddress($address: String!, $stageNo: Int!) {
btcPlusRewardByAddress(address: $address, stageNo: $stageNo) {
balance
balanceUSD
rewardPower
currentTotalRewardPower
estimatedReward
__typename
}
}
"""
variables = {"stageNo": stage_no, "address": address}
payload = {
"operationName": "BtcPlusRewardByAddress",
"query": query,
"variables": variables,
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
data["timestamp"] = get_current_time() # 添加当前时间
return data
def fetch_btc_plus_allocations():
"""从API获取BTC Plus分配数据"""
url = "https://graphql.sft-api.com/graphql"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0",
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Referer": "https://app.solv.finance/",
"content-type": "application/json",
"authorization": "dW5kZWZpbmVkfHx1bmRlZmluZWR8fHVuZGVmaW5lZHx8.undefined",
"x-amz-user-agent": "aws-amplify/3.0.7",
"Origin": "https://app.solv.finance",
"DNT": "1",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "cross-site",
"Sec-GPC": "1",
"Priority": "u=4",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
"TE": "trailers",
}
query = """
query BtcPlusAllocations {
btcPlusAllocations {
tvl
allocations {
assetName
percentage
color
__typename
}
__typename
}
}
"""
payload = {
"operationName": "BtcPlusAllocations",
"query": query,
"variables": {},
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
data["timestamp"] = get_current_time() # 添加当前时间
return data
def fetch_btc_plus_stats(stage_no=1):
"""从API获取BTC Plus统计数据"""
url = "https://graphql.sft-api.com/graphql"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0",
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Referer": "https://app.solv.finance/",
"content-type": "application/json",
"authorization": "dW5kZWZpbmVkfHx1bmRlZmluZWR8fHVuZGVmaW5lZHx8.undefined",
"x-amz-user-agent": "aws-amplify/3.0.7",
"Origin": "https://app.solv.finance",
"DNT": "1",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "cross-site",
"Sec-GPC": "1",
"Priority": "u=4",
}
query = """
query BtcPlusStats($stageNo: Int) {
btcPlusStats(stageNo: $stageNo) {
baseApy
rewardApy
tvl
cap
lastUpdatedTime
startDate
endDate
currentTotalRewardPower
totalRewards
__typename
}
}
"""
variables = {"stageNo": stage_no}
payload = {
"operationName": "BtcPlusStats",
"query": query,
"variables": variables,
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
data["timestamp"] = get_current_time() # 添加当前时间
return data
def fetch_phase2_points(address):
"""从API获取Phase2积分系统数据"""
url = "https://graphql.sft-api.com/graphql"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0",
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Referer": "https://app.solv.finance/",
"content-type": "application/json",
"authorization": "dW5kZWZpbmVkfHx1bmRlZmluZWR8fHVuZGVmaW5lZHx8.undefined",
"x-amz-user-agent": "aws-amplify/3.0.7",
"Origin": "https://app.solv.finance",
"DNT": "1",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "cross-site",
"Sec-GPC": "1",
"Priority": "u=4",
}
query = """
query Phase2PointSysAccountInfo($address: String) {
phase2PointSysAccountInfo(address: $address) {
address
isRegistered
seedUserInviteCode
inviteCode
inviteCount
totalPointsEarned
availablePoints
isPointsAccelerationActive
todayHoldingTVL
todayHoldingAccelerationRatio
nextLevelHoldingTVL
nextLevelHoldingAccelerationRatio
activityCards {
type
accelerationRatio
startTime
endTime
__typename
}
isHighestLevel
__typename
}
}
"""
variables = {"address": address}
payload = {
"operationName": "Phase2PointSysAccountInfo",
"query": query,
"variables": variables,
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
data["timestamp"] = get_current_time()
return data
def save_to_csv(data, filename="btc_plus_rewards.csv"):
"""将数据保存到CSV文件"""
logger = logging.getLogger(__name__)
timestamp = data.get("timestamp", get_current_time())
if "btcPlusRewardByAddress" in data.get("data", {}):
reward_data = data["data"]["btcPlusRewardByAddress"]
row = {
"timestamp": timestamp,
"address": data.get("variables", {}).get("address", "unknown"),
"stageNo": data.get("variables", {}).get("stageNo", "unknown"),
"balance": reward_data["balance"],
"balanceUSD": reward_data["balanceUSD"],
"rewardPower": reward_data["rewardPower"],
"currentTotalRewardPower": reward_data["currentTotalRewardPower"],
"estimatedReward": reward_data["estimatedReward"],
}
filename = "btc_plus_rewards.csv"
elif "btcPlusAllocations" in data.get("data", {}):
allocation_data = data["data"]["btcPlusAllocations"]
row = {
"timestamp": timestamp,
"tvl": allocation_data["tvl"],
"allocations": ";".join(
f"{a['assetName']}:{a['percentage']}"
for a in allocation_data["allocations"]
),
}
filename = "btc_plus_allocations.csv"
elif "phase2PointSysAccountInfo" in data.get("data", {}):
points_data = data["data"]["phase2PointSysAccountInfo"]
row = {
"timestamp": timestamp,
"address": points_data["address"],
"isRegistered": points_data["isRegistered"],
"inviteCode": points_data["inviteCode"],
"inviteCount": points_data["inviteCount"],
"totalPointsEarned": points_data["totalPointsEarned"],
"availablePoints": points_data["availablePoints"],
"isPointsAccelerationActive": points_data["isPointsAccelerationActive"],
"todayHoldingTVL": points_data["todayHoldingTVL"],
"todayHoldingAccelerationRatio": points_data["todayHoldingAccelerationRatio"],
"nextLevelHoldingTVL": points_data["nextLevelHoldingTVL"],
"nextLevelHoldingAccelerationRatio": points_data["nextLevelHoldingAccelerationRatio"],
"isHighestLevel": points_data["isHighestLevel"],
}
filename = "phase2_points.csv"
else:
stats_data = data["data"]["btcPlusStats"]
row = {
"timestamp": timestamp,
"stageNo": data.get("variables", {}).get("stageNo", "unknown"),
"baseApy": stats_data["baseApy"],
"rewardApy": stats_data["rewardApy"],
"tvl": stats_data["tvl"],
"cap": stats_data["cap"],
"lastUpdatedTime": timestamp_to_datetime(stats_data["lastUpdatedTime"]),
"startDate": timestamp_to_datetime(stats_data["startDate"]),
"endDate": timestamp_to_datetime(stats_data["endDate"]),
"currentTotalRewardPower": stats_data["currentTotalRewardPower"],
"totalRewards": stats_data["totalRewards"],
}
filename = "btc_plus_stats.csv"
# 写入CSV文件
try:
with open(filename, "a", newline="") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=row.keys())
# 如果是新文件,写入表头
if csvfile.tell() == 0:
writer.writeheader()
writer.writerow(row)
except Exception as e:
logger.error("保存到CSV时出错: %s", e)
# 初始化日志
def setup_logging():
"""配置日志记录,只记录错误信息"""
logging.basicConfig(
level=logging.WARNING, # 只记录WARNING及以上级别
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(LOG_FILE, encoding="utf-8"),
],
)
return logging.getLogger(__name__)
def git_commit_and_push():
"""自动提交并推送数据更新到Git仓库"""
logger = logging.getLogger(__name__)
try:
subprocess.run(["git", "add", "."], check=True)
subprocess.run(
["git", "commit", "-m", str(datetime.now())],
check=True,
)
subprocess.run(["git", "push"], check=True)
logger.info("Git提交成功")
except subprocess.CalledProcessError as e:
logger.error("Git操作失败: %s", e)
def main_loop():
"""主循环,定期获取数据并更新"""
logger = setup_logging()
logger.info("BTC Plus数据收集程序启动")
last_git_time = last_points_time = last_stats_time = last_rewards_time = last_allocations_time = 0
while True:
try:
current_time = time.time()
# 获取并保存所有数据
address = "0x1bFD4C5bBD6d3eabDeec1Ece32Fb2dFC8f28C6b0"
stage_no = 1
if current_time - last_rewards_time >= REWARDS_INTERVAL:
reward_data = fetch_btc_plus_reward(address, stage_no)
reward_data["variables"] = {"address": address, "stageNo": stage_no}
save_to_csv(reward_data)
last_rewards_time = current_time
if current_time - last_allocations_time >= ALLOCATIONS_INTERVAL:
allocation_data = fetch_btc_plus_allocations()
save_to_csv(allocation_data)
last_allocations_time = current_time
if current_time - last_stats_time >= STATS_INTERVAL:
stats_data = fetch_btc_plus_stats(stage_no)
stats_data["variables"] = {"stageNo": stage_no}
save_to_csv(stats_data)
last_stats_time = current_time
if current_time - last_points_time >= POINTS_INTERVAL:
points_data = fetch_phase2_points(address)
save_to_csv(points_data)
last_points_time = current_time
# 定时Git提交
if current_time - last_git_time >= GIT_INTERVAL:
git_commit_and_push()
last_git_time = current_time
time.sleep(FETCH_INTERVAL)
except Exception as e:
logger.error("发生错误: %s1秒后重试...", e)
time.sleep(1)
if __name__ == "__main__":
main_loop()