This commit is contained in:
2025-08-02 19:49:52 +08:00
commit 822fb7113a

312
main.py Normal file
View File

@@ -0,0 +1,312 @@
import requests
import csv
from datetime import datetime
import subprocess
import time
import logging
from pathlib import Path
# 配置常量
GIT_INTERVAL = 3600 # Git提交间隔(秒)
FETCH_INTERVAL = 3600 # 数据获取间隔(秒)
LOG_FILE = "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 save_to_csv(data, filename="btc_plus_rewards.csv"):
"""将数据保存到CSV文件"""
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"
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)
print(f"数据已成功保存到 {filename}")
except Exception as e:
print(f"保存到CSV时出错: {e}")
# 初始化日志
def setup_logging():
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(LOG_FILE, encoding="utf-8"),
logging.StreamHandler(),
],
)
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", f"Auto update BTC Plus data at {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 = 0
while True:
try:
# 获取并保存所有数据
address = "0x1bFD4C5bBD6d3eabDeec1Ece32Fb2dFC8f28C6b0"
stage_no = 1
reward_data = fetch_btc_plus_reward(address, stage_no)
reward_data["variables"] = {"address": address, "stageNo": stage_no}
save_to_csv(reward_data)
allocation_data = fetch_btc_plus_allocations()
save_to_csv(allocation_data)
stats_data = fetch_btc_plus_stats(stage_no)
stats_data["variables"] = {"stageNo": stage_no}
save_to_csv(stats_data)
# 定时Git提交
current_time = time.time()
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("发生错误: %s10秒后重试...", e)
time.sleep(10)
if __name__ == "__main__":
main_loop()