SMS abuse guard: balance floor, concurrency cap, hourly/daily caps, auto-expire
This commit is contained in:
36
app.py
36
app.py
@@ -278,6 +278,28 @@ def api_card():
|
||||
"type": bl.get("type"), "prepaid": bl.get("prepaid")},
|
||||
"flags": (["prepaid-risk"] if (bl.get("type")=="prepaid" or bl.get("prepaid") is True) else []) + (["luhn-invalid"] if not ok else [])})
|
||||
|
||||
# ---------- SMS ABUSE GUARD ----------
|
||||
def sms_guard():
|
||||
"""Return None if allowed, else a JSON-able reason string."""
|
||||
con = db(); now = int(time.time())
|
||||
ip = request.headers.get("X-Real-IP") or request.remote_addr or "?"
|
||||
# balance: refuse if house SMSPool balance below $5
|
||||
st, b = sms_api("balance")
|
||||
bal = jf(b) or {}
|
||||
try: bal = float(bal.get("balance", 0))
|
||||
except Exception: bal = 0
|
||||
if bal < 5: return f"house balance too low (${bal:.2f}) — rentals paused"
|
||||
# per-IP: max 1 active rental, 3/hour, 8/day
|
||||
row = con.execute("SELECT COUNT(*) c FROM sms_rentals WHERE status='active' AND purchase_id IN (SELECT purchase_id FROM sms_rentals WHERE created > ?)", (now-86400*7,)).fetchone()
|
||||
# active count overall (any IP) cap 3 concurrent
|
||||
act = con.execute("SELECT COUNT(*) c FROM sms_rentals WHERE status='active' AND expires > ?", (now,)).fetchone()["c"]
|
||||
if act >= 3: return "too many active rentals right now — try again later"
|
||||
h = con.execute("SELECT COUNT(*) c FROM sms_rentals WHERE created > ?", (now-3600,)).fetchone()["c"]
|
||||
if h >= 6: return "hourly rental cap reached"
|
||||
d = con.execute("SELECT COUNT(*) c FROM sms_rentals WHERE created > ?", (now-86400,)).fetchone()["c"]
|
||||
if d >= 15: return "daily rental cap reached"
|
||||
return None
|
||||
|
||||
# ---------- 3. SMS RENTALS ----------
|
||||
SMSP = "https://api.smspool.net"
|
||||
SERVICES = [("google","Google"),("discord","Discord"),("telegram","Telegram"),("whatsapp","WhatsApp"),("other","Other/Any")]
|
||||
@@ -296,6 +318,10 @@ def sms():
|
||||
if request.method == "POST":
|
||||
act = request.form.get("act")
|
||||
if act == "rent":
|
||||
guard = sms_guard()
|
||||
if guard:
|
||||
msg = f'<div class="card"><span class="tag warn">PAUSED</span> {guard}</div>'
|
||||
else:
|
||||
svc, ctry = request.form["service"], request.form["country"]
|
||||
st, b = sms_api("purchase/sms", service=svc, country=ctry)
|
||||
d = jf(b) or {}
|
||||
@@ -360,12 +386,14 @@ if(d.sms||d.code){{toast('CODE: '+(d.sms||d.code));document.title='✉ CODE '+(d
|
||||
|
||||
@app.route("/api/sms/rent", methods=["POST"])
|
||||
def api_sms_rent():
|
||||
st, b = sms_api("purchase/sms", service=request.form["service"], country=request.form["country"])
|
||||
guard = sms_guard()
|
||||
if guard: return jsonify({"success": 0, "message": guard, "paused": True})
|
||||
st, b = sms_api("purchase/sms", service=param("service"), country=param("country"))
|
||||
d = jf(b) or {}
|
||||
if d.get("success") == 1:
|
||||
con = db(); now = int(time.time())
|
||||
con.execute("INSERT INTO sms_rentals(phone,service,country,purchase_id,cost,status,created,expires) VALUES(?,?,?,?,?,?,?,?)",
|
||||
(d.get("number"), request.form["service"], request.form["country"], str(d.get("purchase_id")), d.get("cost",0), "active", now, now+1800))
|
||||
(d.get("number"), param("service"), param("country"), str(d.get("purchase_id")), d.get("cost",0), "active", now, now+1800))
|
||||
con.commit()
|
||||
return jsonify(d)
|
||||
|
||||
@@ -381,7 +409,9 @@ def api_sms_cancel():
|
||||
|
||||
@app.route("/api/sms/history")
|
||||
def api_sms_history():
|
||||
con = db()
|
||||
con = db(); now = int(time.time())
|
||||
con.execute("UPDATE sms_rentals SET status='expired' WHERE status='active' AND expires < ?", (now,))
|
||||
con.commit()
|
||||
rows = con.execute("SELECT * FROM sms_rentals ORDER BY id DESC LIMIT 50").fetchall()
|
||||
return jsonify([dict(r) for r in rows])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user