rrr4 / app /api /endpoints /users.py
minaewrw's picture
Initial backend deployment for Hugging Face Spaces
11757af
from fastapi import APIRouter, HTTPException, Query
from typing import Optional
import uuid
from ...models.schemas import UserStatus
from ...core.database import db_manager
import logging
import time
router = APIRouter(prefix="/user", tags=["users"])
logger = logging.getLogger("api.users")
@router.post("/init", response_model=UserStatus)
async def init_user(user_id: Optional[str] = None, referrer_id: Optional[str] = None):
async with db_manager.get_connection() as db:
if not user_id:
user_id = str(uuid.uuid4())
# Check if user exists
cursor = await db.execute("SELECT * FROM users WHERE id = ?", (user_id,))
user_row = await cursor.fetchone()
if not user_row:
# Check user count for first 200 reward
cursor = await db.execute("SELECT COUNT(*) FROM users")
count = (await cursor.fetchone())[0]
welcome_points = 100
is_rewarded = 0
if count < 200:
welcome_points = 10000
is_rewarded = 1
logger.info(f"New User {user_id} awarded 10,000 pts (Count: {count})")
await db.execute(
"INSERT INTO users (id, referrer_id, points, is_rewarded) VALUES (?, ?, ?, ?)",
(user_id, referrer_id, welcome_points, is_rewarded)
)
if referrer_id:
await db.execute(
"INSERT OR IGNORE INTO referrals (referrer_id, referred_id, status) VALUES (?, ?, 'joined')",
(referrer_id, user_id)
)
await db.execute("UPDATE users SET points = points + 50 WHERE id = ?", (referrer_id,))
await db.commit()
else:
# Handle existing users who missed the reward
user = dict(user_row)
if not user.get('is_rewarded'):
cursor = await db.execute("SELECT COUNT(*) FROM users")
count = (await cursor.fetchone())[0]
if count < 200:
await db.execute(
"UPDATE users SET points = points + 10000, is_rewarded = 1 WHERE id = ?",
(user_id,)
)
await db.commit()
logger.info(f"Existing User {user_id} awarded 10,000 pts (Count: {count})")
cursor = await db.execute("SELECT * FROM users WHERE id = ?", (user_id,))
user = await cursor.fetchone()
return dict(user)
@router.get("/status/{user_id}", response_model=UserStatus)
async def get_user_status(user_id: str):
async with db_manager.get_connection() as db:
cursor = await db.execute("SELECT * FROM users WHERE id = ?", (user_id,))
user = await cursor.fetchone()
if not user:
return await init_user(user_id)
return dict(user)
@router.post("/watch")
async def track_watch(user_id: str, minutes: int = 5):
"""Real system: 1 Hour = 120 Points (2 points per minute)"""
points_per_min = 2
pts_to_add = minutes * points_per_min
async with db_manager.get_connection() as db:
await db.execute(
"UPDATE users SET watch_time_total = watch_time_total + ?, points = points + ? WHERE id = ?",
(minutes * 60, pts_to_add, user_id)
)
# Performance Milestone: Every 2 real hours of watching award 200 extra bonus points
cursor = await db.execute("SELECT watch_time_total, last_watch_reward_time, points FROM users WHERE id = ?", (user_id,))
user_at = await cursor.fetchone()
milestone = 2 * 3600 # 2 hours
if user_at["watch_time_total"] // milestone > user_at["last_watch_reward_time"] // milestone:
await db.execute(
"UPDATE users SET points = points + 200, last_watch_reward_time = ? WHERE id = ?",
(user_at["watch_time_total"], user_id)
)
await db.commit()
return {"status": "success", "added": pts_to_add, "total_points": user_at["points"] + pts_to_add}
@router.post("/share-click")
async def register_share_click(referrer_id: str):
"""Award points for just clicks on shared links"""
async with db_manager.get_connection() as db:
await db.execute(
"UPDATE users SET points = points + 5 WHERE id = ?",
(referrer_id,)
)
await db.commit()
return {"status": "success"}
@router.post("/redeem")
async def redeem_reward(user_id: str, reward_type: str):
"""Redemption logic: Daily, Weekly, Monthly ad-free subscriptions"""
costs = {
"ad_free_1d": 700, # 1 Day
"ad_free_1w": 3000, # 1 Week
"ad_free_1m": 10000, # 1 Month
"fan_badge": 100000 # Permanent Ultra Fan Badge
}
durations = {
"ad_free_1d": 1 * 24 * 3600,
"ad_free_1w": 7 * 24 * 3600,
"ad_free_1m": 30 * 24 * 3600
}
cost = costs.get(reward_type)
if not cost:
raise HTTPException(status_code=400, detail="Invalid reward type")
async with db_manager.get_connection() as db:
cursor = await db.execute("SELECT points, ad_free_until FROM users WHERE id = ?", (user_id,))
user = await cursor.fetchone()
if not user or user["points"] < cost:
raise HTTPException(status_code=403, detail="Insufficient points")
if reward_type.startswith("ad_free"):
now = int(time.time())
base_time = max(now, user["ad_free_until"] or 0)
new_expiry = base_time + durations[reward_type]
await db.execute(
"UPDATE users SET points = points - ?, ad_free_until = ? WHERE id = ?",
(cost, new_expiry, user_id)
)
elif reward_type == "fan_badge":
await db.execute(
"UPDATE users SET points = points - ?, is_fan = 1 WHERE id = ?",
(cost, user_id)
)
await db.commit()
return {"status": "success", "reward": reward_type, "new_points": user["points"] - cost}
@router.post("/redeem-promo")
async def redeem_promo(user_id: str, code: str):
"""Redeem a promo code for ad-free subscription"""
code = code.strip()
now = int(time.time())
# Special Hardcoded Codes
if code == "movidovip1":
# Lifetime ad-free
lifetime_expiry = 2147483647 # Year 2038 (standard unix limit)
async with db_manager.get_connection() as db:
await db.execute("UPDATE users SET ad_free_until = ? WHERE id = ?", (lifetime_expiry, user_id))
await db.commit()
return {"status": "success", "reward": "lifetime", "new_until": lifetime_expiry}
if code == "vip-movido34":
# 90 days, 300 uses limit
async with db_manager.get_connection() as db:
# Check if user already used this code
cursor = await db.execute("SELECT * FROM user_promos WHERE user_id = ? AND code = ?", (user_id, code))
if await cursor.fetchone():
raise HTTPException(status_code=400, detail="لقد استخدمت هذا الكود بالفعل")
# Check usage count
cursor = await db.execute("SELECT COUNT(*) FROM user_promos WHERE code = ?", (code,))
usage_count = (await cursor.fetchone())[0]
if usage_count >= 300:
raise HTTPException(status_code=400, detail="عذراً، وصل هذا الكود للحد الأقصى من الاستخدام")
# Award 90 days
cursor = await db.execute("SELECT ad_free_until FROM users WHERE id = ?", (user_id,))
user = await cursor.fetchone()
current_until = max(user["ad_free_until"] or 0, now)
new_until = current_until + (90 * 24 * 3600)
await db.execute("INSERT INTO user_promos (user_id, code) VALUES (?, ?)", (user_id, code))
await db.execute("UPDATE users SET ad_free_until = ? WHERE id = ?", (new_until, user_id))
await db.commit()
return {"status": "success", "reward": "90_days", "new_until": new_until}
# Standard DB codes (backup)
async with db_manager.get_connection() as db:
cursor = await db.execute("SELECT * FROM promo_codes WHERE code = ? AND is_active = 1", (code,))
promo = await cursor.fetchone()
if not promo:
raise HTTPException(status_code=404, detail="كود غير صالح")
if promo["current_uses"] >= promo["max_uses"]:
raise HTTPException(status_code=400, detail="انتهت صلاحية هذا الكود")
# Check if user already used it
cursor = await db.execute("SELECT * FROM user_promos WHERE user_id = ? AND code = ?", (user_id, code))
if await cursor.fetchone():
raise HTTPException(status_code=400, detail="لقد استخدمت هذا الكود بالفعل")
duration = (promo["duration_days"] or 30) * 24 * 3600
cursor = await db.execute("SELECT ad_free_until FROM users WHERE id = ?", (user_id,))
user = await cursor.fetchone()
current_until = max(user["ad_free_until"] or 0, now)
new_until = current_until + duration
await db.execute("INSERT INTO user_promos (user_id, code) VALUES (?, ?)", (user_id, code))
await db.execute("UPDATE promo_codes SET current_uses = current_uses + 1 WHERE code = ?", (code,))
await db.execute("UPDATE users SET ad_free_until = ? WHERE id = ?", (new_until, user_id))
await db.commit()
return {"status": "success", "reward": promo["reward_type"], "new_until": new_until}
@router.post("/history")
async def save_history(user_id: str, content_id: str, content_type: str, episode_id: Optional[str] = None, progress: int = 0):
async with db_manager.get_connection() as db:
# Check if entry exists for this user/content combination to avoid duplicates or just log everything?
# User request: "store the link I watched... and make seo"
# We will insert a new record for every watch session to track frequency,
# OR update the last seen time. Let's do INSERT for full history log.
await db.execute(
"""INSERT INTO watch_history (user_id, content_id, content_type, episode_id, progress)
VALUES (?, ?, ?, ?, ?)""",
(user_id, content_id, content_type, episode_id, progress)
)
await db.commit()
return {"status": "saved"}
@router.get("/history/{user_id}")
async def get_history(user_id: str):
async with db_manager.get_connection() as db:
cursor = await db.execute(
"""SELECT h.*, s.title, s.poster
FROM watch_history h
LEFT JOIN series s ON h.content_id = s.id AND h.content_type = 'series'
-- Join movies if we had a movies table populated similarly
WHERE h.user_id = ?
ORDER BY h.watched_at DESC LIMIT 50""",
(user_id,)
)
history = await cursor.fetchall()
return [dict(row) for row in history]