Files
solv-btc-plus/main.py

398 lines
12 KiB
Python
Raw Permalink 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.
import requests
import csv
from datetime import datetime
import subprocess
import time
import logging
# 配置常量
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 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", f"Auto update 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)
points_data = fetch_phase2_points(address)
save_to_csv(points_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()