saq1b commited on
Commit
fa9a763
ยท
verified ยท
1 Parent(s): 2b118b0

Upload main.py

Browse files
Files changed (1) hide show
  1. main.py +190 -3
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