Upload main.py
Browse files
main.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import socket
|
2 |
socket.setdefaulttimeout(4000)
|
3 |
|
4 |
-
from fastapi import FastAPI, HTTPException
|
5 |
from fastapi.middleware.cors import CORSMiddleware
|
6 |
from pydantic import BaseModel
|
7 |
from typing import Optional, Any, Dict, List
|
@@ -73,7 +73,11 @@ SHEET_UPDATE_DELAY_SECONDS = 10 # 10 second delay between sheet updates
|
|
73 |
SCAMMER_WEBHOOK_URL = os.getenv("SCAMMER_WEBHOOK_URL")
|
74 |
VALUE_WEBHOOK_URL = os.getenv("VALUE_WEBHOOK_URL")
|
75 |
DUPE_CHECK_WEBHOOK_URL = os.getenv("DUPE_CHECK_WEBHOOK_URL")
|
|
|
76 |
|
|
|
|
|
|
|
77 |
|
78 |
# --- Global Cache ---
|
79 |
cache = {
|
@@ -86,7 +90,9 @@ cache = {
|
|
86 |
"dupes": [], # List of duped usernames
|
87 |
"last_updated": None, # Timestamp of the last successful/partial update
|
88 |
"is_ready": False, # Is the cache populated at least once?
|
89 |
-
"service_available": True # Is the Google Sheets service reachable?
|
|
|
|
|
90 |
}
|
91 |
# --- Google Sheets Initialization ---
|
92 |
sheets_service = None # Initialize as None
|
@@ -1007,7 +1013,10 @@ async def startup_event():
|
|
1007 |
logger.warning("VALUE_WEBHOOK_URL environment variable not set. Value change notifications disabled.")
|
1008 |
if not DUPE_CHECK_WEBHOOK_URL:
|
1009 |
logger.warning("WEBHOOK_URL (for dupe checks) environment variable not set. Dupe check notifications disabled.")
|
|
|
|
|
1010 |
asyncio.create_task(update_cache_periodically())
|
|
|
1011 |
|
1012 |
|
1013 |
# --- API Endpoints ---
|
@@ -1176,4 +1185,182 @@ def health_check():
|
|
1176 |
return status_detail
|
1177 |
|
1178 |
# If we reach here, status is 'ok'
|
1179 |
-
return status_detail
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import socket
|
2 |
socket.setdefaulttimeout(4000)
|
3 |
|
4 |
+
from fastapi import FastAPI, HTTPException, Request
|
5 |
from fastapi.middleware.cors import CORSMiddleware
|
6 |
from pydantic import BaseModel
|
7 |
from typing import Optional, Any, Dict, List
|
|
|
73 |
SCAMMER_WEBHOOK_URL = os.getenv("SCAMMER_WEBHOOK_URL")
|
74 |
VALUE_WEBHOOK_URL = os.getenv("VALUE_WEBHOOK_URL")
|
75 |
DUPE_CHECK_WEBHOOK_URL = os.getenv("DUPE_CHECK_WEBHOOK_URL")
|
76 |
+
VISITOR_WEBHOOK_URL = os.getenv("VISITOR_WEBHOOK_URL") # New webhook URL for visitor tracking
|
77 |
|
78 |
+
# Visitor tracking configuration
|
79 |
+
VISITOR_BATCH_INTERVAL_SECONDS = 60 # Send visitor webhooks every 1 minute
|
80 |
+
MAX_VISITORS_PER_WEBHOOK = 3 # Maximum number of visitors to include in a single webhook
|
81 |
|
82 |
# --- Global Cache ---
|
83 |
cache = {
|
|
|
90 |
"dupes": [], # List of duped usernames
|
91 |
"last_updated": None, # Timestamp of the last successful/partial update
|
92 |
"is_ready": False, # Is the cache populated at least once?
|
93 |
+
"service_available": True, # Is the Google Sheets service reachable?
|
94 |
+
"visitors": [], # Recent visitors for batched webhook notifications
|
95 |
+
"last_visitor_webhook": None # Timestamp of last visitor webhook
|
96 |
}
|
97 |
# --- Google Sheets Initialization ---
|
98 |
sheets_service = None # Initialize as None
|
|
|
1013 |
logger.warning("VALUE_WEBHOOK_URL environment variable not set. Value change notifications disabled.")
|
1014 |
if not DUPE_CHECK_WEBHOOK_URL:
|
1015 |
logger.warning("WEBHOOK_URL (for dupe checks) environment variable not set. Dupe check notifications disabled.")
|
1016 |
+
if not VISITOR_WEBHOOK_URL:
|
1017 |
+
logger.warning("VISITOR_WEBHOOK_URL environment variable not set. Visitor tracking notifications disabled.")
|
1018 |
asyncio.create_task(update_cache_periodically())
|
1019 |
+
asyncio.create_task(visitor_webhook_task()) # Start visitor webhook background task
|
1020 |
|
1021 |
|
1022 |
# --- API Endpoints ---
|
|
|
1185 |
return status_detail
|
1186 |
|
1187 |
# If we reach here, status is 'ok'
|
1188 |
+
return status_detail
|
1189 |
+
|
1190 |
+
# --- Visitor Tracking Model and Endpoint ---
|
1191 |
+
class VisitorData(BaseModel):
|
1192 |
+
device_type: str # "Laptop", "Desktop", "Mobile", etc.
|
1193 |
+
os: str # "Windows 10", "MacOS", etc.
|
1194 |
+
browser: str # "Chrome", "Firefox", etc.
|
1195 |
+
country: str # Country of origin
|
1196 |
+
path: str # URL path visited
|
1197 |
+
ip_address: Optional[str] = None # Optional IP address (for internal use only)
|
1198 |
+
|
1199 |
+
@app.post("/api/track-visitor")
|
1200 |
+
async def track_visitor(visitor: VisitorData, request: Request):
|
1201 |
+
"""Records visitor information and adds it to the visitor queue"""
|
1202 |
+
|
1203 |
+
# Get client IP (for internal tracking, not included in webhook)
|
1204 |
+
client_host = request.client.host if request.client else "Unknown"
|
1205 |
+
|
1206 |
+
# Format current time
|
1207 |
+
current_time = datetime.now()
|
1208 |
+
formatted_time = current_time.strftime("%A, %B %d, %Y %I:%M %p")
|
1209 |
+
short_time = current_time.strftime("%I:%M %p")
|
1210 |
+
|
1211 |
+
# Get country flag emoji
|
1212 |
+
flag_emoji = "๐" # Default globe emoji
|
1213 |
+
if visitor.country.lower() == "france":
|
1214 |
+
flag_emoji = "๐ซ๐ท"
|
1215 |
+
elif visitor.country.lower() == "pakistan":
|
1216 |
+
flag_emoji = "๐ต๐ฐ"
|
1217 |
+
elif visitor.country.lower() == "united states" or visitor.country.lower() == "usa":
|
1218 |
+
flag_emoji = "๐บ๐ธ"
|
1219 |
+
elif visitor.country.lower() == "united kingdom" or visitor.country.lower() == "uk":
|
1220 |
+
flag_emoji = "๐ฌ๐ง"
|
1221 |
+
elif visitor.country.lower() == "canada":
|
1222 |
+
flag_emoji = "๐จ๐ฆ"
|
1223 |
+
elif visitor.country.lower() == "australia":
|
1224 |
+
flag_emoji = "๐ฆ๐บ"
|
1225 |
+
elif visitor.country.lower() == "germany":
|
1226 |
+
flag_emoji = "๐ฉ๐ช"
|
1227 |
+
elif visitor.country.lower() == "india":
|
1228 |
+
flag_emoji = "๐ฎ๐ณ"
|
1229 |
+
elif visitor.country.lower() == "japan":
|
1230 |
+
flag_emoji = "๐ฏ๐ต"
|
1231 |
+
elif visitor.country.lower() == "china":
|
1232 |
+
flag_emoji = "๐จ๐ณ"
|
1233 |
+
# Add more country flags as needed
|
1234 |
+
|
1235 |
+
# Create visitor entry
|
1236 |
+
visitor_entry = {
|
1237 |
+
"device_type": visitor.device_type,
|
1238 |
+
"os": visitor.os,
|
1239 |
+
"browser": visitor.browser,
|
1240 |
+
"country": visitor.country,
|
1241 |
+
"flag_emoji": flag_emoji,
|
1242 |
+
"path": visitor.path,
|
1243 |
+
"timestamp": current_time,
|
1244 |
+
"formatted_time": short_time,
|
1245 |
+
"ip_address": client_host # Store IP internally but don't send in webhook
|
1246 |
+
}
|
1247 |
+
|
1248 |
+
# Add to visitor queue
|
1249 |
+
cache["visitors"].append(visitor_entry)
|
1250 |
+
logger.info(f"Recorded visitor from {visitor.country} using {visitor.browser} visiting {visitor.path}")
|
1251 |
+
|
1252 |
+
# Check if we should send a webhook immediately
|
1253 |
+
should_send_now = len(cache["visitors"]) >= MAX_VISITORS_PER_WEBHOOK
|
1254 |
+
|
1255 |
+
if cache["last_visitor_webhook"]:
|
1256 |
+
time_since_last = (current_time - cache["last_visitor_webhook"]).total_seconds()
|
1257 |
+
should_send_now = should_send_now or time_since_last >= VISITOR_BATCH_INTERVAL_SECONDS
|
1258 |
+
else:
|
1259 |
+
# First visitor, send after accumulating more or after interval
|
1260 |
+
should_send_now = False
|
1261 |
+
|
1262 |
+
# Send webhook if needed
|
1263 |
+
if should_send_now and VISITOR_WEBHOOK_URL:
|
1264 |
+
await send_visitor_webhook()
|
1265 |
+
|
1266 |
+
return {"status": "recorded", "webhook_queued": True}
|
1267 |
+
|
1268 |
+
async def send_visitor_webhook():
|
1269 |
+
"""Sends a webhook with accumulated visitor information"""
|
1270 |
+
if not cache["visitors"] or not VISITOR_WEBHOOK_URL:
|
1271 |
+
return False
|
1272 |
+
|
1273 |
+
try:
|
1274 |
+
current_time = datetime.now()
|
1275 |
+
|
1276 |
+
# Group visitors by device type for better organization
|
1277 |
+
visitors_by_device = {}
|
1278 |
+
for visitor in cache["visitors"]:
|
1279 |
+
key = f"{visitor['device_type']} - {visitor['os']} - {visitor['browser']}"
|
1280 |
+
if key not in visitors_by_device:
|
1281 |
+
visitors_by_device[key] = []
|
1282 |
+
visitors_by_device[key].append(visitor)
|
1283 |
+
|
1284 |
+
# Create webhook fields for each device group
|
1285 |
+
fields = []
|
1286 |
+
for device_key, visitors in visitors_by_device.items():
|
1287 |
+
visits_text = ""
|
1288 |
+
for v in visitors:
|
1289 |
+
# Format the path more specifically as shown in the example
|
1290 |
+
path_display = v['path']
|
1291 |
+
if "textures" in path_display.lower():
|
1292 |
+
path_display = "www.JailbreakValueCentral.com/textures"
|
1293 |
+
elif "tires" in path_display.lower() or "rims" in path_display.lower():
|
1294 |
+
path_display = "www.JailbreakValueCentral.net/tires"
|
1295 |
+
elif "calculator" in path_display.lower():
|
1296 |
+
path_display = "www.JailbreakValueCentral.net/value-calculator"
|
1297 |
+
elif "drift" in path_display.lower() or "particles" in path_display.lower():
|
1298 |
+
path_display = "www.JailbreakValueCentral.net/drift-particles"
|
1299 |
+
elif "furniture" in path_display.lower():
|
1300 |
+
path_display = "www.JailbreakValueCentral.net/furniture"
|
1301 |
+
elif "spoilers" in path_display.lower():
|
1302 |
+
path_display = "www.JailbreakValueCentral.net/spoilers"
|
1303 |
+
|
1304 |
+
# Format date like: Sunday, April 13, 2025 2:06 AM
|
1305 |
+
timestamp_str = v['timestamp'].strftime("%A, %B %d, %Y %I:%M %p")
|
1306 |
+
|
1307 |
+
# Format similar to the example in the screenshot
|
1308 |
+
visits_text += f"A User from {v['country']} {v['flag_emoji']} browsed:\n"
|
1309 |
+
visits_text += f"{timestamp_str}:\n"
|
1310 |
+
visits_text += f"{path_display}\n\n"
|
1311 |
+
|
1312 |
+
fields.append({
|
1313 |
+
"name": device_key,
|
1314 |
+
"value": visits_text.strip(),
|
1315 |
+
"inline": False
|
1316 |
+
})
|
1317 |
+
|
1318 |
+
# Create the embed with improved wording
|
1319 |
+
embed = {
|
1320 |
+
"title": "๐ Jailbreak Value Central Visitors",
|
1321 |
+
"description": "Check out the latest visitor activity from around the world! See what sections are trending right now.",
|
1322 |
+
"color": 3447003, # Blue
|
1323 |
+
"fields": fields,
|
1324 |
+
"timestamp": current_time.isoformat(),
|
1325 |
+
"footer": {
|
1326 |
+
"text": f"Sourced From JailbreakValueCentral.com โข Today at {current_time.strftime('%I:%M %p')}"
|
1327 |
+
},
|
1328 |
+
"thumbnail": {
|
1329 |
+
"url": "https://jailbreakvalue.net/logo.png" # Update with your actual logo URL
|
1330 |
+
}
|
1331 |
+
}
|
1332 |
+
|
1333 |
+
# Send the webhook notification
|
1334 |
+
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session:
|
1335 |
+
await send_webhook_notification(session, VISITOR_WEBHOOK_URL, embed)
|
1336 |
+
logger.info(f"Visitor tracking webhook sent with {len(cache['visitors'])} entries")
|
1337 |
+
|
1338 |
+
# Clear the visitor queue and update timestamp
|
1339 |
+
cache["visitors"] = []
|
1340 |
+
cache["last_visitor_webhook"] = current_time
|
1341 |
+
|
1342 |
+
return True
|
1343 |
+
|
1344 |
+
except Exception as e:
|
1345 |
+
logger.error(f"Error sending visitor tracking webhook: {e}")
|
1346 |
+
return False
|
1347 |
+
|
1348 |
+
# Background task to periodically send visitor webhooks
|
1349 |
+
async def visitor_webhook_task():
|
1350 |
+
"""Periodically sends visitor webhooks if there are any pending"""
|
1351 |
+
while True:
|
1352 |
+
try:
|
1353 |
+
current_time = datetime.now()
|
1354 |
+
|
1355 |
+
# Check if we have visitors and if enough time has passed
|
1356 |
+
if cache["visitors"] and VISITOR_WEBHOOK_URL:
|
1357 |
+
if not cache["last_visitor_webhook"] or \
|
1358 |
+
(current_time - cache["last_visitor_webhook"]).total_seconds() >= VISITOR_BATCH_INTERVAL_SECONDS:
|
1359 |
+
await send_visitor_webhook()
|
1360 |
+
|
1361 |
+
# Wait for next check
|
1362 |
+
await asyncio.sleep(60) # Check every minute
|
1363 |
+
|
1364 |
+
except Exception as e:
|
1365 |
+
logger.error(f"Error in visitor webhook background task: {e}")
|
1366 |
+
await asyncio.sleep(60) # Wait and retry
|