fix: dynamic days remaining via tariff_end_at timestamp; auto-downgrade when expired
This commit is contained in:
+34
-16
@@ -81,6 +81,12 @@ def clear_cache(sub_id: str = None):
|
|||||||
_links_cache.clear()
|
_links_cache.clear()
|
||||||
_traffic_cache.clear()
|
_traffic_cache.clear()
|
||||||
|
|
||||||
|
def get_remaining_days(user: dict) -> int:
|
||||||
|
end = user.get("tariff_end_at", 0)
|
||||||
|
if end:
|
||||||
|
return max(0, (end - int(time.time())) // 86400)
|
||||||
|
return user.get("tariff_days_remaining", 0)
|
||||||
|
|
||||||
def load_json(path: str, default: dict) -> dict:
|
def load_json(path: str, default: dict) -> dict:
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
try:
|
try:
|
||||||
@@ -107,12 +113,19 @@ def init_db():
|
|||||||
tier TEXT DEFAULT 'free',
|
tier TEXT DEFAULT 'free',
|
||||||
tariff_days_bought INTEGER DEFAULT 0,
|
tariff_days_bought INTEGER DEFAULT 0,
|
||||||
tariff_days_remaining INTEGER DEFAULT 0,
|
tariff_days_remaining INTEGER DEFAULT 0,
|
||||||
|
tariff_end_at INTEGER DEFAULT 0,
|
||||||
total_paid_rubles INTEGER DEFAULT 0,
|
total_paid_rubles INTEGER DEFAULT 0,
|
||||||
traffic_limit_gb INTEGER DEFAULT 0,
|
traffic_limit_gb INTEGER DEFAULT 0,
|
||||||
is_active BOOLEAN DEFAULT 1,
|
is_active BOOLEAN DEFAULT 1,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
try:
|
||||||
|
conn.execute("ALTER TABLE users ADD COLUMN tariff_end_at INTEGER DEFAULT 0")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
now = int(time.time())
|
||||||
|
conn.execute("UPDATE users SET tariff_end_at = ? + tariff_days_remaining * 86400 WHERE tariff_end_at = 0 AND tariff_days_remaining > 0", (now,))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
@@ -376,7 +389,7 @@ VALUES (?, ?, 'free', 0, 0, 0, 0, 1)
|
|||||||
|
|
||||||
user = dict(user)
|
user = dict(user)
|
||||||
|
|
||||||
if user['tier'] == 'paid' and user['tariff_days_remaining'] <= 0:
|
if user['tier'] == 'paid' and get_remaining_days(user) <= 0:
|
||||||
conn.execute("UPDATE users SET tier = 'free' WHERE id = ?", (user['id'],))
|
conn.execute("UPDATE users SET tier = 'free' WHERE id = ?", (user['id'],))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
user['tier'] = 'free'
|
user['tier'] = 'free'
|
||||||
@@ -554,8 +567,9 @@ async def get_subscription(request: Request, subscription_id: str, format: str =
|
|||||||
lines.append(f"#support-url: {support_url}")
|
lines.append(f"#support-url: {support_url}")
|
||||||
|
|
||||||
expire_ts = 0
|
expire_ts = 0
|
||||||
if user.get("tariff_days_remaining", 0) > 0:
|
rem_days = get_remaining_days(user)
|
||||||
expire_ts = int((datetime.now() + timedelta(days=user["tariff_days_remaining"])).timestamp())
|
if rem_days > 0:
|
||||||
|
expire_ts = int((datetime.now() + timedelta(days=rem_days)).timestamp())
|
||||||
lines.append("#sub-expire: 1")
|
lines.append("#sub-expire: 1")
|
||||||
if support_url:
|
if support_url:
|
||||||
lines.append(f"#sub-expire-button-link: {support_url}")
|
lines.append(f"#sub-expire-button-link: {support_url}")
|
||||||
@@ -624,7 +638,7 @@ async def get_web_page(subscription_id: str):
|
|||||||
tier_name = tier_config.get("name", "Free")
|
tier_name = tier_config.get("name", "Free")
|
||||||
tier_badge = f'<span class="tier-badge" style="background: {tier_color}">{tier_name}</span>'
|
tier_badge = f'<span class="tier-badge" style="background: {tier_color}">{tier_name}</span>'
|
||||||
|
|
||||||
days_remaining = user.get("tariff_days_remaining", 0)
|
days_remaining = get_remaining_days(user)
|
||||||
days_info = f"<p>⏳ Осталось дней: {days_remaining}</p>" if tier == "paid" and days_remaining > 0 else ""
|
days_info = f"<p>⏳ Осталось дней: {days_remaining}</p>" if tier == "paid" and days_remaining > 0 else ""
|
||||||
|
|
||||||
traffic = get_cached_traffic(subscription_id)
|
traffic = get_cached_traffic(subscription_id)
|
||||||
@@ -728,14 +742,15 @@ async def webhook_donationalerts(request: Request):
|
|||||||
|
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
try:
|
try:
|
||||||
|
now_ts = int(time.time())
|
||||||
conn.execute("""
|
conn.execute("""
|
||||||
UPDATE users SET
|
UPDATE users SET
|
||||||
tier = ?,
|
tier = ?,
|
||||||
tariff_days_bought = tariff_days_bought + ?,
|
tariff_days_bought = tariff_days_bought + ?,
|
||||||
tariff_days_remaining = tariff_days_remaining + ?,
|
tariff_end_at = MAX(COALESCE(tariff_end_at, 0), ?) + ? * 86400,
|
||||||
total_paid_rubles = total_paid_rubles + ?
|
total_paid_rubles = total_paid_rubles + ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
""", (tier, days, days, amount, user["id"]))
|
""", (tier, days, now_ts, days, amount, user["id"]))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -764,18 +779,19 @@ async def admin_users(request: Request):
|
|||||||
traffic = f'{u["traffic_limit_gb"]} GB' if u['traffic_limit_gb'] > 0 else '∞'
|
traffic = f'{u["traffic_limit_gb"]} GB' if u['traffic_limit_gb'] > 0 else '∞'
|
||||||
active = '✓' if u['is_active'] else '✗'
|
active = '✓' if u['is_active'] else '✗'
|
||||||
tier_display = u['tier'].upper()
|
tier_display = u['tier'].upper()
|
||||||
|
rem_days = get_remaining_days(u)
|
||||||
users_rows += f'''<tr>
|
users_rows += f'''<tr>
|
||||||
<td>{u['id']}</td>
|
<td>{u['id']}</td>
|
||||||
<td style="font-weight:600;color:var(--text)">{u['username']}</td>
|
<td style="font-weight:600;color:var(--text)">{u['username']}</td>
|
||||||
<td><code>{u['subscription_id']}</code></td>
|
<td><code>{u['subscription_id']}</code></td>
|
||||||
<td><span class="badge badge-{u['tier']}">{tier_display}</span></td>
|
<td><span class="badge badge-{u['tier']}">{tier_display}</span></td>
|
||||||
<td>{u['tariff_days_remaining']}</td>
|
<td>{rem_days}</td>
|
||||||
<td>{u['total_paid_rubles']}₽</td>
|
<td>{u['total_paid_rubles']}₽</td>
|
||||||
<td>{traffic}</td>
|
<td>{traffic}</td>
|
||||||
<td>{active}</td>
|
<td>{active}</td>
|
||||||
<td>{u['created_at'][:10]}</td>
|
<td>{u['created_at'][:10]}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn-sm" onclick="editUser({u['id']}, '{u['tier']}', {u['tariff_days_remaining']}, {u['traffic_limit_gb']}, {1 if u['is_active'] else 0})">✍️</button>
|
<button class="btn-sm" onclick="editUser({u['id']}, '{u['tier']}', {rem_days}, {u['traffic_limit_gb']}, {1 if u['is_active'] else 0})">✍️</button>
|
||||||
<button class="btn-sm danger" onclick="deleteUser({u['id']}, '{u['username']}')">🗑️</button>
|
<button class="btn-sm danger" onclick="deleteUser({u['id']}, '{u['username']}')">🗑️</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>'''
|
</tr>'''
|
||||||
@@ -1185,6 +1201,7 @@ async def update_user(request: Request, data: dict):
|
|||||||
user_id = int(data.get("id", 0))
|
user_id = int(data.get("id", 0))
|
||||||
tier = str(data.get("tier", "free"))
|
tier = str(data.get("tier", "free"))
|
||||||
tariff_days_remaining = int(data.get("tariff_days_remaining", 0) or 0)
|
tariff_days_remaining = int(data.get("tariff_days_remaining", 0) or 0)
|
||||||
|
tariff_end_at = int(time.time()) + tariff_days_remaining * 86400 if tariff_days_remaining > 0 else 0
|
||||||
traffic_limit_gb = int(data.get("traffic_limit_gb", 0) or 0)
|
traffic_limit_gb = int(data.get("traffic_limit_gb", 0) or 0)
|
||||||
is_active = 1 if data.get("is_active") in [True, "true", "on", "1", 1] else 0
|
is_active = 1 if data.get("is_active") in [True, "true", "on", "1", 1] else 0
|
||||||
|
|
||||||
@@ -1193,9 +1210,9 @@ async def update_user(request: Request, data: dict):
|
|||||||
conn = get_db()
|
conn = get_db()
|
||||||
try:
|
try:
|
||||||
conn.execute("""
|
conn.execute("""
|
||||||
UPDATE users SET tier = ?, tariff_days_remaining = ?, traffic_limit_gb = ?, is_active = ?
|
UPDATE users SET tier = ?, tariff_days_remaining = ?, tariff_end_at = ?, traffic_limit_gb = ?, is_active = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
""", (tier, tariff_days_remaining, traffic_limit_gb, is_active, user_id))
|
""", (tier, tariff_days_remaining, tariff_end_at, traffic_limit_gb, is_active, user_id))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
updated = conn.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
|
updated = conn.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
|
||||||
@@ -1374,20 +1391,21 @@ async def poll_donationalerts():
|
|||||||
user = conn.execute("SELECT * FROM users WHERE username = ? COLLATE NOCASE", (username,)).fetchone()
|
user = conn.execute("SELECT * FROM users WHERE username = ? COLLATE NOCASE", (username,)).fetchone()
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
current_expiry = user.get("tariff_days_remaining", 0)
|
now_ts = int(time.time())
|
||||||
if current_expiry > 0:
|
current_end = user.get("tariff_end_at", 0)
|
||||||
new_expiry = current_expiry + days
|
if current_end > now_ts:
|
||||||
|
new_end = current_end + days * 86400
|
||||||
else:
|
else:
|
||||||
new_expiry = days
|
new_end = now_ts + days * 86400
|
||||||
|
|
||||||
conn.execute("""
|
conn.execute("""
|
||||||
UPDATE users SET
|
UPDATE users SET
|
||||||
tier = ?,
|
tier = ?,
|
||||||
tariff_days_bought = tariff_days_bought + ?,
|
tariff_days_bought = tariff_days_bought + ?,
|
||||||
tariff_days_remaining = ?,
|
tariff_end_at = ?,
|
||||||
total_paid_rubles = total_paid_rubles + ?
|
total_paid_rubles = total_paid_rubles + ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
""", (tier, days, new_expiry, amount, user["id"]))
|
""", (tier, days, new_end, amount, user["id"]))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
sub_id = user["subscription_id"]
|
sub_id = user["subscription_id"]
|
||||||
|
|||||||
Reference in New Issue
Block a user