Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,7 +12,7 @@ USERNAME = os.getenv("APP_USERNAME", "未设置")
|
|
| 12 |
PASSWORD = os.getenv("APP_PASSWORD", "未设置")
|
| 13 |
BASE_URL = "https://free-api.rorryson.com"
|
| 14 |
|
| 15 |
-
# 强制 Python 立即输出日志,不缓存
|
| 16 |
sys.stdout.reconfigure(encoding='utf-8')
|
| 17 |
|
| 18 |
# 初始化界面状态
|
|
@@ -22,13 +22,12 @@ if 'status_msg' not in st.session_state:
|
|
| 22 |
st.session_state.status_msg = "系统就绪 (等待指令)"
|
| 23 |
|
| 24 |
def log_print(msg):
|
| 25 |
-
"""日志助手
|
| 26 |
timestamp = datetime.now().strftime("%H:%M:%S")
|
| 27 |
-
# flush=True 确保日志立即出现在 Hugging Face 的 Logs 标签页中
|
| 28 |
print(f"[{timestamp}] {msg}", flush=True)
|
| 29 |
return timestamp
|
| 30 |
|
| 31 |
-
# ================= 2. 核心签到逻辑
|
| 32 |
def perform_checkin():
|
| 33 |
log_print("🚀 --- 开始执行任务 ---")
|
| 34 |
|
|
@@ -38,8 +37,7 @@ def perform_checkin():
|
|
| 38 |
|
| 39 |
session = requests.Session()
|
| 40 |
|
| 41 |
-
#
|
| 42 |
-
# 包含 Sec-Fetch 系列,这是防止 401 的关键
|
| 43 |
base_headers = {
|
| 44 |
"Host": "free-api.rorryson.com",
|
| 45 |
"Connection": "keep-alive",
|
|
@@ -58,46 +56,35 @@ def perform_checkin():
|
|
| 58 |
session.headers.update(base_headers)
|
| 59 |
|
| 60 |
try:
|
| 61 |
-
# ---
|
| 62 |
-
|
| 63 |
-
session.headers.update({
|
| 64 |
-
"Referer": "https://free-api.rorryson.com/login",
|
| 65 |
-
"New-API-User": "-1"
|
| 66 |
-
})
|
| 67 |
-
|
| 68 |
log_print("正在尝试登录...")
|
| 69 |
res_login = session.post(f"{BASE_URL}/api/user/login?turnstile=", json={"username": USERNAME, "password": PASSWORD})
|
| 70 |
|
| 71 |
if res_login.status_code != 200:
|
| 72 |
-
log_print(f"登录失败: {res_login.status_code}
|
| 73 |
st.session_state.status_msg = f"❌ 登录失败 ({res_login.status_code})"
|
| 74 |
return
|
| 75 |
|
| 76 |
log_print("✅ 登录成功,Session Cookie 已获取")
|
| 77 |
|
| 78 |
-
# ---
|
| 79 |
-
# 切换到签到环境,特征头必须变
|
| 80 |
session.headers.update({
|
| 81 |
"Referer": "https://free-api.rorryson.com/console/topup",
|
| 82 |
-
"New-API-User": "201",
|
| 83 |
-
"Cache-Control": "no-store"
|
| 84 |
})
|
| 85 |
-
|
| 86 |
-
time.sleep(1) # 模拟人类操作延迟
|
| 87 |
|
| 88 |
log_print("正在尝试签到...")
|
| 89 |
res_checkin = session.post(f"{BASE_URL}/api/user/checkin")
|
| 90 |
-
|
| 91 |
ts = datetime.now().strftime("%H:%M:%S")
|
| 92 |
|
| 93 |
-
# 处理各种签到结果
|
| 94 |
if res_checkin.status_code == 200:
|
| 95 |
try:
|
| 96 |
data = res_checkin.json()
|
| 97 |
msg = data.get("message", "OK")
|
| 98 |
-
log_print(f"签到返回
|
| 99 |
-
|
| 100 |
-
# 智能判断中文提示
|
| 101 |
if "已" in msg or "duplicate" in msg.lower():
|
| 102 |
st.session_state.status_msg = f"🔸 {msg} ({ts})"
|
| 103 |
else:
|
|
@@ -106,27 +93,32 @@ def perform_checkin():
|
|
| 106 |
st.session_state.status_msg = f"✅ 签到请求成功 ({ts})"
|
| 107 |
|
| 108 |
elif res_checkin.status_code == 422:
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
st.session_state.status_msg = f"🔸 今天已签到过 (无需重复) ({ts})"
|
| 112 |
-
|
| 113 |
else:
|
| 114 |
log_print(f"签到异常: {res_checkin.status_code}")
|
| 115 |
st.session_state.status_msg = f"⚠️ 接口返回 {res_checkin.status_code} ({ts})"
|
| 116 |
|
| 117 |
-
# ---
|
| 118 |
-
# 保持 Headers 不变
|
| 119 |
res_info = session.get(f"{BASE_URL}/api/user/self")
|
| 120 |
if res_info.status_code == 200:
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
except Exception as e:
|
| 126 |
log_print(f"程序崩溃: {str(e)}")
|
| 127 |
-
st.session_state.status_msg = "💥 系统内部错误
|
| 128 |
|
| 129 |
-
# ================= 3. 后台调度
|
| 130 |
def run_scheduler():
|
| 131 |
while True:
|
| 132 |
schedule.run_pending()
|
|
@@ -135,30 +127,21 @@ def run_scheduler():
|
|
| 135 |
def start_background_thread():
|
| 136 |
if 'scheduler_started' not in st.session_state:
|
| 137 |
log_print("⚡ 系统启动初始化...")
|
| 138 |
-
|
| 139 |
-
# 1. 启动时立即尝试一次 (方便你看日志确认是否成功)
|
| 140 |
perform_checkin()
|
| 141 |
-
|
| 142 |
-
# 2. 设定每天 UTC 00:00 (北京时间 08:00) 自动运行
|
| 143 |
schedule.every().day.at("00:00").do(perform_checkin)
|
| 144 |
-
|
| 145 |
-
# 3. 启动线程
|
| 146 |
t = threading.Thread(target=run_scheduler, daemon=True)
|
| 147 |
t.start()
|
| 148 |
st.session_state.scheduler_started = True
|
| 149 |
log_print("⏰ 定时任务已设定: 每天北京时间 08:00")
|
| 150 |
|
| 151 |
-
# ================= 4. 前端界面
|
| 152 |
st.set_page_config(page_title="R-API 哨兵", page_icon="🛡️", layout="centered")
|
| 153 |
|
| 154 |
-
# 必须调用启动函数
|
| 155 |
start_background_thread()
|
| 156 |
|
| 157 |
-
# 赛博风格 CSS
|
| 158 |
st.markdown("""
|
| 159 |
<style>
|
| 160 |
.stApp {background-color: #050505; color: #ccc;}
|
| 161 |
-
/* 隐藏不需要的组件 */
|
| 162 |
#MainMenu {visibility: hidden;}
|
| 163 |
footer {visibility: hidden;}
|
| 164 |
|
|
@@ -168,7 +151,9 @@ st.markdown("""
|
|
| 168 |
box-shadow: 0 4px 15px rgba(0,0,0,0.5);
|
| 169 |
}
|
| 170 |
.metric-label {font-size: 0.9rem; color: #666; margin-bottom: 5px;}
|
| 171 |
-
|
|
|
|
|
|
|
| 172 |
|
| 173 |
.status-box {
|
| 174 |
margin-top: 25px; padding: 15px; border-radius: 8px;
|
|
@@ -176,8 +161,6 @@ st.markdown("""
|
|
| 176 |
font-family: "Microsoft YaHei", sans-serif; color: #00c6ff;
|
| 177 |
text-align: center;
|
| 178 |
}
|
| 179 |
-
|
| 180 |
-
/* 按钮美化 */
|
| 181 |
.stButton>button {
|
| 182 |
width: 100%; height: 50px; background: linear-gradient(90deg, #00c6ff, #0072ff);
|
| 183 |
border: none; color: white; font-weight: bold; font-size: 1rem; border-radius: 8px;
|
|
@@ -185,7 +168,6 @@ st.markdown("""
|
|
| 185 |
</style>
|
| 186 |
""", unsafe_allow_html=True)
|
| 187 |
|
| 188 |
-
# 界面布局
|
| 189 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 190 |
|
| 191 |
col1, col2 = st.columns(2)
|
|
@@ -197,25 +179,22 @@ with col1:
|
|
| 197 |
</div>
|
| 198 |
""", unsafe_allow_html=True)
|
| 199 |
with col2:
|
| 200 |
-
# 余额根据是否有数值变色
|
| 201 |
color = "#00ffcc" if st.session_state.quota != "---" else "#444"
|
| 202 |
st.markdown(f"""
|
| 203 |
<div class="metric-card">
|
| 204 |
-
<div class="metric-label">当前账户余额</div>
|
| 205 |
<div class="metric-val" style="color:{color}">{st.session_state.quota}</div>
|
| 206 |
</div>
|
| 207 |
""", unsafe_allow_html=True)
|
| 208 |
|
| 209 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 210 |
|
| 211 |
-
# 手动签到按钮
|
| 212 |
if st.button("🚀 立即执行手动签到", use_container_width=True):
|
| 213 |
with st.spinner("正在连接服务器..."):
|
| 214 |
perform_checkin()
|
| 215 |
time.sleep(0.5)
|
| 216 |
st.rerun()
|
| 217 |
|
| 218 |
-
# 状态显示区
|
| 219 |
st.markdown(f"""
|
| 220 |
<div class="status-box">
|
| 221 |
系统状态: {st.session_state.status_msg}
|
|
@@ -226,3 +205,4 @@ st.markdown(f"""
|
|
| 226 |
|
| 227 |
|
| 228 |
|
|
|
|
|
|
| 12 |
PASSWORD = os.getenv("APP_PASSWORD", "未设置")
|
| 13 |
BASE_URL = "https://free-api.rorryson.com"
|
| 14 |
|
| 15 |
+
# 强制 Python 立即输出日志,不缓存
|
| 16 |
sys.stdout.reconfigure(encoding='utf-8')
|
| 17 |
|
| 18 |
# 初始化界面状态
|
|
|
|
| 22 |
st.session_state.status_msg = "系统就绪 (等待指令)"
|
| 23 |
|
| 24 |
def log_print(msg):
|
| 25 |
+
"""日志助手"""
|
| 26 |
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
|
|
| 27 |
print(f"[{timestamp}] {msg}", flush=True)
|
| 28 |
return timestamp
|
| 29 |
|
| 30 |
+
# ================= 2. 核心签到逻辑 =================
|
| 31 |
def perform_checkin():
|
| 32 |
log_print("🚀 --- 开始执行任务 ---")
|
| 33 |
|
|
|
|
| 37 |
|
| 38 |
session = requests.Session()
|
| 39 |
|
| 40 |
+
# 基础 Headers (防 401)
|
|
|
|
| 41 |
base_headers = {
|
| 42 |
"Host": "free-api.rorryson.com",
|
| 43 |
"Connection": "keep-alive",
|
|
|
|
| 56 |
session.headers.update(base_headers)
|
| 57 |
|
| 58 |
try:
|
| 59 |
+
# --- 步骤 1: 登录 ---
|
| 60 |
+
session.headers.update({"Referer": "https://free-api.rorryson.com/login", "New-API-User": "-1"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
log_print("正在尝试登录...")
|
| 62 |
res_login = session.post(f"{BASE_URL}/api/user/login?turnstile=", json={"username": USERNAME, "password": PASSWORD})
|
| 63 |
|
| 64 |
if res_login.status_code != 200:
|
| 65 |
+
log_print(f"登录失败: {res_login.status_code}")
|
| 66 |
st.session_state.status_msg = f"❌ 登录失败 ({res_login.status_code})"
|
| 67 |
return
|
| 68 |
|
| 69 |
log_print("✅ 登录成功,Session Cookie 已获取")
|
| 70 |
|
| 71 |
+
# --- 步骤 2: 签到 ---
|
|
|
|
| 72 |
session.headers.update({
|
| 73 |
"Referer": "https://free-api.rorryson.com/console/topup",
|
| 74 |
+
"New-API-User": "201",
|
| 75 |
+
"Cache-Control": "no-store"
|
| 76 |
})
|
| 77 |
+
time.sleep(1)
|
|
|
|
| 78 |
|
| 79 |
log_print("正在尝试签到...")
|
| 80 |
res_checkin = session.post(f"{BASE_URL}/api/user/checkin")
|
|
|
|
| 81 |
ts = datetime.now().strftime("%H:%M:%S")
|
| 82 |
|
|
|
|
| 83 |
if res_checkin.status_code == 200:
|
| 84 |
try:
|
| 85 |
data = res_checkin.json()
|
| 86 |
msg = data.get("message", "OK")
|
| 87 |
+
log_print(f"签到返回: {msg}")
|
|
|
|
|
|
|
| 88 |
if "已" in msg or "duplicate" in msg.lower():
|
| 89 |
st.session_state.status_msg = f"🔸 {msg} ({ts})"
|
| 90 |
else:
|
|
|
|
| 93 |
st.session_state.status_msg = f"✅ 签到请求成功 ({ts})"
|
| 94 |
|
| 95 |
elif res_checkin.status_code == 422:
|
| 96 |
+
log_print("签到状态 422 (重复签到)")
|
| 97 |
+
st.session_state.status_msg = f"🔸 今天已签到过 ({ts})"
|
|
|
|
|
|
|
| 98 |
else:
|
| 99 |
log_print(f"签到异常: {res_checkin.status_code}")
|
| 100 |
st.session_state.status_msg = f"⚠️ 接口返回 {res_checkin.status_code} ({ts})"
|
| 101 |
|
| 102 |
+
# --- 步骤 3: 查余额 (自动换算) ---
|
|
|
|
| 103 |
res_info = session.get(f"{BASE_URL}/api/user/self")
|
| 104 |
if res_info.status_code == 200:
|
| 105 |
+
raw_quota = res_info.json().get("data", {}).get("quota", 0)
|
| 106 |
+
|
| 107 |
+
# 【核心修改】积分换算逻辑:除以 500,000
|
| 108 |
+
try:
|
| 109 |
+
real_balance = float(raw_quota) / 500000
|
| 110 |
+
# 格式化显示: 加$符号,��留2位小数,加千位分隔符
|
| 111 |
+
st.session_state.quota = f"${real_balance:,.2f}"
|
| 112 |
+
except:
|
| 113 |
+
st.session_state.quota = str(raw_quota)
|
| 114 |
+
|
| 115 |
+
log_print(f"原始积分: {raw_quota} -> 换算显示: {st.session_state.quota}")
|
| 116 |
|
| 117 |
except Exception as e:
|
| 118 |
log_print(f"程序崩溃: {str(e)}")
|
| 119 |
+
st.session_state.status_msg = "💥 系统内部错误"
|
| 120 |
|
| 121 |
+
# ================= 3. 后台调度 =================
|
| 122 |
def run_scheduler():
|
| 123 |
while True:
|
| 124 |
schedule.run_pending()
|
|
|
|
| 127 |
def start_background_thread():
|
| 128 |
if 'scheduler_started' not in st.session_state:
|
| 129 |
log_print("⚡ 系统启动初始化...")
|
|
|
|
|
|
|
| 130 |
perform_checkin()
|
|
|
|
|
|
|
| 131 |
schedule.every().day.at("00:00").do(perform_checkin)
|
|
|
|
|
|
|
| 132 |
t = threading.Thread(target=run_scheduler, daemon=True)
|
| 133 |
t.start()
|
| 134 |
st.session_state.scheduler_started = True
|
| 135 |
log_print("⏰ 定时任务已设定: 每天北京时间 08:00")
|
| 136 |
|
| 137 |
+
# ================= 4. 前端界面 =================
|
| 138 |
st.set_page_config(page_title="R-API 哨兵", page_icon="🛡️", layout="centered")
|
| 139 |
|
|
|
|
| 140 |
start_background_thread()
|
| 141 |
|
|
|
|
| 142 |
st.markdown("""
|
| 143 |
<style>
|
| 144 |
.stApp {background-color: #050505; color: #ccc;}
|
|
|
|
| 145 |
#MainMenu {visibility: hidden;}
|
| 146 |
footer {visibility: hidden;}
|
| 147 |
|
|
|
|
| 151 |
box-shadow: 0 4px 15px rgba(0,0,0,0.5);
|
| 152 |
}
|
| 153 |
.metric-label {font-size: 0.9rem; color: #666; margin-bottom: 5px;}
|
| 154 |
+
|
| 155 |
+
/* 余额字体显示 */
|
| 156 |
+
.metric-val {font-size: 1.8rem; font-weight: bold; color: #fff; font-family: monospace;}
|
| 157 |
|
| 158 |
.status-box {
|
| 159 |
margin-top: 25px; padding: 15px; border-radius: 8px;
|
|
|
|
| 161 |
font-family: "Microsoft YaHei", sans-serif; color: #00c6ff;
|
| 162 |
text-align: center;
|
| 163 |
}
|
|
|
|
|
|
|
| 164 |
.stButton>button {
|
| 165 |
width: 100%; height: 50px; background: linear-gradient(90deg, #00c6ff, #0072ff);
|
| 166 |
border: none; color: white; font-weight: bold; font-size: 1rem; border-radius: 8px;
|
|
|
|
| 168 |
</style>
|
| 169 |
""", unsafe_allow_html=True)
|
| 170 |
|
|
|
|
| 171 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 172 |
|
| 173 |
col1, col2 = st.columns(2)
|
|
|
|
| 179 |
</div>
|
| 180 |
""", unsafe_allow_html=True)
|
| 181 |
with col2:
|
|
|
|
| 182 |
color = "#00ffcc" if st.session_state.quota != "---" else "#444"
|
| 183 |
st.markdown(f"""
|
| 184 |
<div class="metric-card">
|
| 185 |
+
<div class="metric-label">当前账户余额 (USD)</div>
|
| 186 |
<div class="metric-val" style="color:{color}">{st.session_state.quota}</div>
|
| 187 |
</div>
|
| 188 |
""", unsafe_allow_html=True)
|
| 189 |
|
| 190 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 191 |
|
|
|
|
| 192 |
if st.button("🚀 立即执行手动签到", use_container_width=True):
|
| 193 |
with st.spinner("正在连接服务器..."):
|
| 194 |
perform_checkin()
|
| 195 |
time.sleep(0.5)
|
| 196 |
st.rerun()
|
| 197 |
|
|
|
|
| 198 |
st.markdown(f"""
|
| 199 |
<div class="status-box">
|
| 200 |
系统状态: {st.session_state.status_msg}
|
|
|
|
| 205 |
|
| 206 |
|
| 207 |
|
| 208 |
+
|